diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 4e050986fa5..973921cb24a 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,11 +1,11 @@ blank_issues_enabled: false contact_links: - name: Windows PowerShell - url: https://windowsserver.uservoice.com/forums/301869-powershell + url: https://support.microsoft.com/windows/send-feedback-to-microsoft-with-the-feedback-hub-app-f59187f8-8739-22d6-ba93-f66612949332 about: Windows PowerShell issues or suggestions. - name: Support url: https://github.com/PowerShell/PowerShell/blob/master/.github/SUPPORT.md about: PowerShell Support Questions/Help - name: Documentation Issue - url: https://github.com/MicrosoftDocs/PowerShell-Docs + url: https://github.com/MicrosoftDocs/PowerShell-Docs/issues/new/choose about: Please open issues on documentation for PowerShell here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b64343410f7..6ad155756b5 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -19,7 +19,7 @@ - **[Breaking changes](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#making-breaking-changes)** - [ ] None - **OR** - - [ ] [Experimental feature(s) needed](https://github.com/MicrosoftDocs/PowerShell-Docs/blob/staging/reference/6/Microsoft.PowerShell.Core/About/about_Experimental_Features.md) + - [ ] [Experimental feature(s) needed](https://github.com/MicrosoftDocs/PowerShell-Docs/blob/main/reference/7.3/Microsoft.PowerShell.Core/About/about_Experimental_Features.md) - [ ] Experimental feature name(s): - **User-facing changes** - [ ] Not Applicable diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 38a493b7f36..37c0fc6ca7c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -51,3 +51,10 @@ updates: interval: "daily" labels: - "CL-BuildPackaging" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + labels: + - "CL-BuildPackaging" diff --git a/.github/fabricbot.json b/.github/fabricbot.json new file mode 100644 index 00000000000..6cff68d8c2b --- /dev/null +++ b/.github/fabricbot.json @@ -0,0 +1,2096 @@ +[ + { + "taskType": "trigger", + "capabilityId": "AutoMerge", + "subCapability": "AutoMerge", + "version": "1.0", + "config": { + "taskName": "AutoMerge", + "minMinutesOpen": "1440", + "mergeType": "squash", + "deleteBranches": true, + "label": "AutoMerge", + "minimumNumberOfStatuses": 6, + "requireAllStatuses": false, + "removeLabelOnPush": true, + "requireSpecificCheckRuns": false, + "requireAllStatuses_exemptList": [ + "CodeFactor", + "Codacy/PR Quality Review" + ], + "requireSpecificCheckRunsList": [ + "PowerShell-CI-linux", + "PowerShell-CI-macos", + "PowerShell-CI-static-analysis", + "PowerShell-CI-windows", + "WIP" + ], + "usePrDescriptionAsCommitMessage": true + }, + "disabled": false + }, + { + "taskType": "trigger", + "capabilityId": "ReleaseAnnouncement", + "subCapability": "ReleaseAnnouncement", + "version": "1.0", + "config": { + "taskName": "Announce a fix has been released", + "prReply": ":tada:`${version}` has been released which incorporates this pull request.:tada:\n\nHandy links:\n* [Release Notes](https://github.com/${owner}/${repo}/releases/tag/${version})\n", + "issueReply": ":tada:This issue was addressed in #${prNumber}, which has now been successfully released as `${version}`.:tada:\n\nHandy links:\n* [Release Notes](https://github.com/${owner}/${repo}/releases/tag/${version})\n", + "packageRegex": "(v\\d+\\.\\d+\\.\\d+(-\\w+(\\.\\d+)?)?)", + "referencedPrsRegex": "\\(#(\\d+)\\)", + "packageVersionGroup": 0, + "packageNameGroup": 1 + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestReviewResponder", + "version": "1.0", + "config": { + "taskName": "Add needs author feedback label to pull requests when changes are requested", + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "submitted" + } + }, + { + "name": "isReviewState", + "parameters": { + "state": "changes_requested" + } + } + ] + }, + "actions": [ + { + "name": "addLabel", + "parameters": { + "label": "Waiting on Author" + } + }, + { + "name": "removeMilestone", + "parameters": {} + }, + { + "name": "removeLabel", + "parameters": { + "label": "Review - Needed" + } + } + ], + "eventType": "pull_request", + "eventNames": [ + "pull_request_review" + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "taskName": "Remove needs author feedback label when the author responds to a pull request", + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isActivitySender", + "parameters": { + "user": { + "type": "author" + } + } + }, + { + "operator": "not", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "closed" + } + } + ] + }, + { + "name": "hasLabel", + "parameters": { + "label": "Waiting on Author" + } + }, + { + "operator": "not", + "operands": [ + { + "name": "titleContains", + "parameters": { + "isRegex": true, + "titlePattern": "(WIP|Work in progress|🚧)" + } + } + ] + } + ] + }, + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "Waiting on Author" + } + } + ], + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestCommentResponder", + "version": "1.0", + "config": { + "taskName": "Remove needs author feedback label when the author comments on a pull request", + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isActivitySender", + "parameters": { + "user": { + "type": "author" + } + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "Waiting on Author" + } + } + ] + }, + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "Waiting on Author" + } + } + ], + "eventType": "pull_request", + "eventNames": [ + "issue_comment" + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestReviewResponder", + "version": "1.0", + "config": { + "taskName": "Remove needs author feedback label when the author responds to a pull request review comment", + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isActivitySender", + "parameters": { + "user": { + "type": "author" + } + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "Waiting on Author" + } + } + ] + }, + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "Waiting on Author" + } + } + ], + "eventType": "pull_request", + "eventNames": [ + "pull_request_review" + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "taskName": "Remove no recent activity label from pull requests", + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "closed" + } + } + ] + }, + { + "name": "hasLabel", + "parameters": { + "label": "Stale" + } + }, + { + "name": "isActivitySender", + "parameters": { + "user": { + "type": "author" + } + } + } + ] + }, + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "Stale" + } + } + ], + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestCommentResponder", + "version": "1.0", + "config": { + "taskName": "Remove no recent activity label when a pull request is commented on", + "conditions": { + "operator": "and", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "Stale" + } + } + ] + }, + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "Stale" + } + } + ], + "eventType": "pull_request", + "eventNames": [ + "issue_comment" + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestReviewResponder", + "version": "1.0", + "config": { + "taskName": "Remove no recent activity label when a pull request is reviewed", + "conditions": { + "operator": "and", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "Stale" + } + } + ] + }, + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "Stale" + } + } + ], + "eventType": "pull_request", + "eventNames": [ + "pull_request_review" + ] + } + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "taskName": "Close stale pull requests", + "frequency": [ + { + "weekDay": 0, + "hours": [ + 10, + 22 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 1, + "hours": [ + 10, + 22 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 2, + "hours": [ + 10, + 22 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 3, + "hours": [ + 10, + 22 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 4, + "hours": [ + 10, + 22 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 5, + "hours": [ + 10, + 22 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 6, + "hours": [ + 10, + 22 + ], + "timezoneOffset": -8 + } + ], + "searchTerms": [ + { + "name": "isPr", + "parameters": {} + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "hasLabel", + "parameters": { + "label": "Waiting on Author" + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "Stale" + } + }, + { + "name": "noActivitySince", + "parameters": { + "days": 10 + } + } + ], + "actions": [ + { + "name": "closeIssue", + "parameters": {} + } + ] + } + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "taskName": "Add no recent activity label to pull requests", + "frequency": [ + { + "weekDay": 0, + "hours": [ + 1, + 4, + 7, + 10, + 13, + 16, + 19, + 22 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 1, + "hours": [ + 1, + 4, + 7, + 10, + 13, + 16, + 19, + 22 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 2, + "hours": [ + 1, + 4, + 7, + 10, + 13, + 16, + 19, + 22 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 3, + "hours": [ + 1, + 4, + 7, + 10, + 13, + 16, + 19, + 22 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 4, + "hours": [ + 1, + 4, + 7, + 10, + 13, + 16, + 19, + 22 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 5, + "hours": [ + 1, + 4, + 7, + 10, + 13, + 16, + 19, + 22 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 6, + "hours": [ + 1, + 4, + 7, + 10, + 13, + 16, + 19, + 22 + ], + "timezoneOffset": -7 + } + ], + "searchTerms": [ + { + "name": "isPr", + "parameters": {} + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "hasLabel", + "parameters": { + "label": "Waiting on Author" + } + }, + { + "name": "noActivitySince", + "parameters": { + "days": 15 + } + }, + { + "name": "noLabel", + "parameters": { + "label": "Stale" + } + } + ], + "actions": [ + { + "name": "addLabel", + "parameters": { + "label": "Stale" + } + }, + { + "name": "addReply", + "parameters": { + "comment": "This pull request has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for **15 days**. It will be closed if no further activity occurs **within 10 days of this comment**." + } + }, + { + "name": "removeMilestone", + "parameters": {} + } + ] + } + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "taskName": "Close duplicate issues", + "frequency": [ + { + "weekDay": 0, + "hours": [ + 0, + 3, + 6, + 9, + 12, + 15, + 18, + 21 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 1, + "hours": [ + 0, + 3, + 6, + 9, + 12, + 15, + 18, + 21 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 2, + "hours": [ + 0, + 3, + 6, + 9, + 12, + 15, + 18, + 21 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 3, + "hours": [ + 0, + 3, + 6, + 9, + 12, + 15, + 18, + 21 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 4, + "hours": [ + 0, + 3, + 6, + 9, + 12, + 15, + 18, + 21 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 5, + "hours": [ + 0, + 3, + 6, + 9, + 12, + 15, + 18, + 21 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 6, + "hours": [ + 0, + 3, + 6, + 9, + 12, + 15, + 18, + 21 + ], + "timezoneOffset": -8 + } + ], + "searchTerms": [ + { + "name": "isIssue", + "parameters": {} + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "hasLabel", + "parameters": { + "label": "Resolution-Duplicate" + } + }, + { + "name": "noActivitySince", + "parameters": { + "days": 1 + } + } + ], + "actions": [ + { + "name": "addReply", + "parameters": { + "comment": "This issue has been marked as duplicate and has not had any activity for **1 day**. It has been closed for housekeeping purposes." + } + }, + { + "name": "closeIssue", + "parameters": {} + } + ] + }, + "disabled": false + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "taskName": "Close external issues", + "frequency": [ + { + "weekDay": 0, + "hours": [ + 2, + 5, + 8, + 11, + 14, + 17, + 20, + 23 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 1, + "hours": [ + 2, + 5, + 8, + 11, + 14, + 17, + 20, + 23 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 2, + "hours": [ + 2, + 5, + 8, + 11, + 14, + 17, + 20, + 23 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 3, + "hours": [ + 2, + 5, + 8, + 11, + 14, + 17, + 20, + 23 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 4, + "hours": [ + 2, + 5, + 8, + 11, + 14, + 17, + 20, + 23 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 5, + "hours": [ + 2, + 5, + 8, + 11, + 14, + 17, + 20, + 23 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 6, + "hours": [ + 2, + 5, + 8, + 11, + 14, + 17, + 20, + 23 + ], + "timezoneOffset": -8 + } + ], + "searchTerms": [ + { + "name": "isIssue", + "parameters": {} + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "hasLabel", + "parameters": { + "label": "Resolution-External" + } + }, + { + "name": "noActivitySince", + "parameters": { + "days": 1 + } + } + ], + "actions": [ + { + "name": "addReply", + "parameters": { + "comment": "This issue has been marked as external and has not had any activity for **1 day**. It has been be closed for housekeeping purposes." + } + }, + { + "name": "closeIssue", + "parameters": {} + } + ] + } + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "taskName": "Close answered issues", + "frequency": [ + { + "weekDay": 0, + "hours": [ + 0, + 12 + ] + }, + { + "weekDay": 1, + "hours": [ + 0, + 12 + ] + }, + { + "weekDay": 2, + "hours": [ + 0, + 12 + ] + }, + { + "weekDay": 3, + "hours": [ + 0, + 12 + ] + }, + { + "weekDay": 4, + "hours": [ + 0, + 12 + ] + }, + { + "weekDay": 5, + "hours": [ + 0, + 12 + ] + }, + { + "weekDay": 6, + "hours": [ + 0, + 12 + ] + } + ], + "searchTerms": [ + { + "name": "isIssue", + "parameters": {} + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "hasLabel", + "parameters": { + "label": "Resolution-Answered" + } + }, + { + "name": "noActivitySince", + "parameters": { + "days": 1 + } + } + ], + "actions": [ + { + "name": "addReply", + "parameters": { + "comment": "This issue has been marked as answered and has not had any activity for **1 day**. It has been closed for housekeeping purposes." + } + }, + { + "name": "closeIssue", + "parameters": {} + } + ] + } + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "taskName": "Close fixed issues", + "frequency": [ + { + "weekDay": 0, + "hours": [ + 8, + 20 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 1, + "hours": [ + 8, + 20 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 2, + "hours": [ + 8, + 20 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 3, + "hours": [ + 8, + 20 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 4, + "hours": [ + 8, + 20 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 5, + "hours": [ + 8, + 20 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 6, + "hours": [ + 8, + 20 + ], + "timezoneOffset": -8 + } + ], + "searchTerms": [ + { + "name": "isIssue", + "parameters": {} + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "hasLabel", + "parameters": { + "label": "Resolution-Fixed" + } + }, + { + "name": "noActivitySince", + "parameters": { + "days": 1 + } + } + ], + "actions": [ + { + "name": "addReply", + "parameters": { + "comment": "This issue has been marked as fixed and has not had any activity for **1 day**. It has been closed for housekeeping purposes." + } + }, + { + "name": "closeIssue", + "parameters": {} + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isAssignedToSomeone", + "parameters": {} + } + ] + }, + { + "name": "isAction", + "parameters": { + "action": "opened" + } + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "actions": [ + { + "name": "assignToGitHubUserGroup", + "parameters": { + "groupId": "5dd8713b90bf2e113c0e885e", + "skipOpener": true + } + } + ], + "taskName": "Assign unassigned PRs - on push" + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestCommentResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "operator": "not", + "operands": [ + { + "name": "isAssignedToSomeone", + "parameters": {} + } + ] + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "issue_comment" + ], + "taskName": "Assign unassigned PRs - on comments", + "actions": [ + { + "name": "assignToGitHubUserGroup", + "parameters": { + "groupId": "5dd8713b90bf2e113c0e885e" + } + } + ] + }, + "disabled": true + }, + { + "taskType": "trigger", + "capabilityId": "EmailCleanser", + "subCapability": "EmailCleanser", + "version": "1.0", + "config": { + "taskName": "Email reply cleanser" + } + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "taskName": "Needs attention", + "frequency": [ + { + "weekDay": 0, + "hours": [ + 6, + 18 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 1, + "hours": [ + 6, + 18 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 2, + "hours": [ + 6, + 18 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 3, + "hours": [ + 6, + 18 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 4, + "hours": [ + 6, + 18 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 5, + "hours": [ + 6, + 18 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 6, + "hours": [ + 6, + 18 + ], + "timezoneOffset": -8 + } + ], + "searchTerms": [ + { + "name": "isPr", + "parameters": {} + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "noLabel", + "parameters": { + "label": "Waiting on Author" + } + }, + { + "name": "noActivitySince", + "parameters": { + "days": 7 + } + }, + { + "name": "noLabel", + "parameters": { + "label": "Stale" + } + }, + { + "name": "noLabel", + "parameters": { + "label": "Review - Needed" + } + }, + { + "name": "noLabel", + "parameters": { + "label": "Review - Committee" + } + }, + { + "name": "isDraftPr", + "parameters": { + "value": "false" + } + } + ], + "actions": [ + { + "name": "addLabel", + "parameters": { + "label": "Review - Needed" + } + }, + { + "name": "addReply", + "parameters": { + "comment": "This pull request has been automatically marked as Review Needed because it has been there has not been any activity for **7 days**.\nMaintainer, please provide feedback and/or mark it as `Waiting on Author`" + } + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "Review - Needed" + } + }, + { + "operator": "or", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "merged" + } + }, + { + "name": "isAction", + "parameters": { + "action": "closed" + } + }, + { + "name": "isAction", + "parameters": { + "action": "reopened" + } + }, + { + "name": "isAction", + "parameters": { + "action": "assigned" + } + }, + { + "name": "isAction", + "parameters": { + "action": "unassigned" + } + }, + { + "name": "isAction", + "parameters": { + "action": "unlabeled" + } + } + ] + }, + { + "operator": "or", + "operands": [ + { + "name": "activitySenderHasPermissions", + "parameters": { + "association": "MEMBER", + "permissions": "admin" + } + }, + { + "name": "isActivitySender", + "parameters": { + "user": "iSazonov" + } + } + ] + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "Review - Needed" + } + } + ], + "taskName": "remove review - need when PR updated" + }, + "disabled": false + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestCommentResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "hasLabel", + "parameters": { + "label": "Review - Needed" + } + }, + { + "operator": "or", + "operands": [ + { + "name": "activitySenderHasPermissions", + "parameters": { + "association": "MEMBER", + "permissions": "admin" + } + }, + { + "name": "isActivitySender", + "parameters": { + "user": "iSazonov" + } + } + ] + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "issue_comment" + ], + "taskName": "remove review - need when PR updated - comment created", + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "Review - Needed" + } + } + ] + } + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "frequency": [ + { + "weekDay": 0, + "hours": [ + 0, + 12 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 1, + "hours": [ + 0, + 12 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 2, + "hours": [ + 0, + 12 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 3, + "hours": [ + 0, + 12 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 4, + "hours": [ + 0, + 12 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 5, + "hours": [ + 0, + 12 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 6, + "hours": [ + 0, + 12 + ], + "timezoneOffset": -7 + } + ], + "searchTerms": [ + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "isDraftPr", + "parameters": { + "value": "true" + } + }, + { + "name": "noLabel", + "parameters": { + "label": "Review - Committee" + } + }, + { + "name": "noLabel", + "parameters": { + "label": "Waiting on Author" + } + }, + { + "name": "noLabel", + "parameters": { + "label": "Stale" + } + }, + { + "name": "noActivitySince", + "parameters": { + "days": 3 + } + } + ], + "actions": [ + { + "name": "addLabel", + "parameters": { + "label": "Waiting on Author" + } + } + ], + "taskName": "Label draft PRs as waiting on author" + }, + "disabled": false + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "PullRequestResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "addedToMilestone", + "parameters": {} + }, + { + "name": "isOpen", + "parameters": {} + } + ] + }, + "eventType": "pull_request", + "eventNames": [ + "pull_request", + "issues", + "project_card" + ], + "actions": [ + { + "name": "removeMilestone", + "parameters": {} + }, + { + "name": "addReply", + "parameters": { + "comment": "Open PRs should not be assigned to milestone, so they are not assigned to the wrong milestone after they are merged. For backport consideration, use a `backport` label. " + } + } + ], + "taskName": "Remove label on Open PRs" + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssuesOnlyResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isAction", + "parameters": { + "action": "closed" + } + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issues", + "project_card" + ], + "taskName": "Remove 'Needs-Triage' label when an issue is closed", + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "Needs-Triage" + } + } + ] + } + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "frequency": [ + { + "weekDay": 0, + "hours": [ + 3, + 15 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 1, + "hours": [ + 3, + 15 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 2, + "hours": [ + 3, + 15 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 3, + "hours": [ + 3, + 15 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 4, + "hours": [ + 3, + 15 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 5, + "hours": [ + 3, + 15 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 6, + "hours": [ + 3, + 15 + ], + "timezoneOffset": -7 + } + ], + "searchTerms": [ + { + "name": "isIssue", + "parameters": {} + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "hasLabel", + "parameters": { + "label": "Resolution-Declined" + } + }, + { + "name": "noActivitySince", + "parameters": { + "days": 1 + } + } + ], + "taskName": "Close declined issues", + "actions": [ + { + "name": "addReply", + "parameters": { + "comment": "This issue has been marked as declined and has not had any activity for **1 day**. It has been closed for housekeeping purposes." + } + }, + { + "name": "closeIssue", + "parameters": {} + } + ] + } + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "frequency": [ + { + "weekDay": 0, + "hours": [ + 2, + 14 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 1, + "hours": [ + 2, + 14 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 2, + "hours": [ + 2, + 14 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 3, + "hours": [ + 2, + 14 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 4, + "hours": [ + 2, + 14 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 5, + "hours": [ + 2, + 14 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 6, + "hours": [ + 2, + 14 + ], + "timezoneOffset": -7 + } + ], + "searchTerms": [ + { + "name": "isIssue", + "parameters": {} + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "hasLabel", + "parameters": { + "label": "Resolution-By Design" + } + }, + { + "name": "noActivitySince", + "parameters": { + "days": 1 + } + } + ], + "taskName": "Close by-design issues", + "actions": [ + { + "name": "addReply", + "parameters": { + "comment": "This issue has been marked as by-design and has not had any activity for **1 day**. It has been closed for housekeeping purposes." + } + }, + { + "name": "closeIssue", + "parameters": {} + } + ] + } + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "frequency": [ + { + "weekDay": 0, + "hours": [ + 2, + 14 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 1, + "hours": [ + 2, + 14 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 2, + "hours": [ + 2, + 14 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 3, + "hours": [ + 2, + 14 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 4, + "hours": [ + 2, + 14 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 5, + "hours": [ + 2, + 14 + ], + "timezoneOffset": -7 + }, + { + "weekDay": 6, + "hours": [ + 2, + 14 + ], + "timezoneOffset": -7 + } + ], + "searchTerms": [ + { + "name": "isIssue", + "parameters": {} + }, + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "hasLabel", + "parameters": { + "label": "Resolution-Won't Fix" + } + }, + { + "name": "noActivitySince", + "parameters": { + "days": 1 + } + } + ], + "taskName": "Close won't fix issues", + "actions": [ + { + "name": "addReply", + "parameters": { + "comment": "This issue has been marked as won't fix and has not had any activity for **1 day**. It has been closed for housekeeping purposes." + } + }, + { + "name": "closeIssue", + "parameters": {} + } + ] + } + }, + { + "taskType": "trigger", + "capabilityId": "InPrLabel", + "subCapability": "InPrLabel", + "version": "1.0", + "config": { + "taskName": "Add 'In-PR' label to issue", + "label_inPr": "In-PR", + "fixedLabelEnabled": true, + "label_fixed": "Resolution-Fixed" + } + }, + { + "taskType": "trigger", + "capabilityId": "IssueResponder", + "subCapability": "IssueCommentResponder", + "version": "1.0", + "config": { + "conditions": { + "operator": "and", + "operands": [ + { + "name": "isActivitySender", + "parameters": { + "user": { + "type": "author" + } + } + }, + { + "name": "hasLabel", + "parameters": { + "label": "Waiting on Author" + } + } + ] + }, + "eventType": "issue", + "eventNames": [ + "issue_comment" + ], + "taskName": "Remove needs author feedback label when the author comments on an issue", + "actions": [ + { + "name": "removeLabel", + "parameters": { + "label": "Waiting on Author" + } + } + ] + } + }, + { + "taskType": "scheduled", + "capabilityId": "ScheduledSearch", + "subCapability": "ScheduledSearch", + "version": "1.1", + "config": { + "frequency": [ + { + "weekDay": 0, + "hours": [ + 8, + 20 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 1, + "hours": [ + 8, + 20 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 2, + "hours": [ + 8, + 20 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 3, + "hours": [ + 8, + 20 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 4, + "hours": [ + 8, + 20 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 5, + "hours": [ + 8, + 20 + ], + "timezoneOffset": -8 + }, + { + "weekDay": 6, + "hours": [ + 8, + 20 + ], + "timezoneOffset": -8 + } + ], + "searchTerms": [ + { + "name": "isOpen", + "parameters": {} + }, + { + "name": "isIssue", + "parameters": {} + }, + { + "name": "hasLabel", + "parameters": { + "label": "Waiting on Author" + } + }, + { + "name": "noActivitySince", + "parameters": { + "days": 7 + } + } + ], + "taskName": "Close stale issues", + "actions": [ + { + "name": "addReply", + "parameters": { + "comment": "This issue has been marked as \"Waiting on Author\" and has not had any activity for **7 day**. It has been closed for housekeeping purposes." + } + }, + { + "name": "closeIssue", + "parameters": {} + } + ] + } + } +] diff --git a/.github/prquantifier.yaml b/.github/prquantifier.yaml new file mode 100644 index 00000000000..ea891ba4988 --- /dev/null +++ b/.github/prquantifier.yaml @@ -0,0 +1,11 @@ +# https://github.com/microsoft/PullRequestQuantifier/blob/main/docs/prquantifier-yaml.md +Excluded: +# defaults +- '*.csproj' +- prquantifier.yaml +- package-lock.json +- '*.md' +- '*.sln' +# autogenerated files +- tools/cgmanifest.json +- assets/wix/files.wxs diff --git a/.github/workflows/GHWorkflowHelper/GHWorkflowHelper.psm1 b/.github/workflows/GHWorkflowHelper/GHWorkflowHelper.psm1 new file mode 100644 index 00000000000..f0524ce6f23 --- /dev/null +++ b/.github/workflows/GHWorkflowHelper/GHWorkflowHelper.psm1 @@ -0,0 +1,27 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +function Set-GWVariable { + param( + [Parameter(Mandatory = $true)] + [string]$Name, + [Parameter(Mandatory = $true)] + [string]$Value + ) + + Write-Verbose "Setting CI variable $Name to $Value" -Verbose + + if ($env:GITHUB_ENV) { + "$Name=$Value" | Out-File $env:GITHUB_ENV -Append + } +} + +function Get-GWTempPath { + $temp = [System.IO.Path]::GetTempPath() + if ($env:RUNNER_TEMP) { + $temp = $env:RUNNER_TEMP + } + + Write-Verbose "Get CI Temp path: $temp" -Verbose + return $temp +} diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 00000000000..bfa448413ef --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,61 @@ +name: Backport PR to branch +on: + issue_comment: + types: [created] + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + backport: + if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/backport to') + runs-on: ubuntu-20.04 + steps: + - name: Extract backport target branch + uses: actions/github-script@v6 + id: target-branch-extractor + with: + result-encoding: string + script: | + if (context.eventName !== "issue_comment") throw "Error: This action only works on issue_comment events."; + + // extract the target branch name from the trigger phrase containing these characters: a-z, A-Z, digits, forward slash, dot, hyphen, underscore + const regex = /^\/backport to ([a-zA-Z\d\/\.\-\_]+)/; + target_branch = regex.exec(context.payload.comment.body); + if (target_branch == null) throw "Error: No backport branch found in the trigger phrase."; + + return target_branch[1]; + - name: Post backport started comment to pull request + uses: actions/github-script@v6 + with: + script: | + const backport_start_body = `Started backporting to ${{ steps.target-branch-extractor.outputs.result }}: https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}`; + await github.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: backport_start_body + }); + - name: Checkout repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Run backport + uses: ./tools/actions/backport + with: + target_branch: ${{ steps.target-branch-extractor.outputs.result }} + auth_token: ${{ secrets.GITHUB_TOKEN }} + pr_description_template: | + Backport of #%source_pr_number% to %target_branch% + + /cc %cc_users% + + ## Customer Impact + + ## Testing + + - [ ] For any change that affects the release process, please work with a maintainer to come up with a plan to test this. + + ## Risk diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 903ee79cf4d..48ff5aad04c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -14,8 +14,15 @@ defaults: env: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 +permissions: + contents: read + jobs: analyze: + permissions: + actions: read # for github/codeql-action/init to get workflow details + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/analyze to upload SARIF results name: Analyze runs-on: ubuntu-18.04 @@ -30,13 +37,13 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: '0' # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -59,4 +66,4 @@ jobs: name: Build - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/createReminders.yml b/.github/workflows/createReminders.yml new file mode 100644 index 00000000000..290141703d7 --- /dev/null +++ b/.github/workflows/createReminders.yml @@ -0,0 +1,19 @@ +name: 'Create reminder' + +on: + issue_comment: + types: [created, edited] + +permissions: + contents: read + +jobs: + reminder: + permissions: + issues: write # for agrc/create-reminder-action to set reminders on issues + pull-requests: write # for agrc/create-reminder-action to set reminders on PRs + runs-on: ubuntu-latest + + steps: + - name: check for reminder + uses: agrc/create-reminder-action@v1 diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index a1c883c98f0..dbc85f6ee5b 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -16,41 +16,45 @@ env: DOTNET_CLI_TELEMETRY_OPTOUT: 1 POWERSHELL_TELEMETRY_OPTOUT: 1 +permissions: + contents: read + jobs: update-dotnet-preview: + permissions: + contents: write # for peter-evans/create-pull-request to create branch + pull-requests: write # for peter-evans/create-pull-request to create a PR name: Update .NET preview timeout-minutes: 15 runs-on: windows-latest if: github.repository == 'PowerShell/PowerShell' steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Sync tags run: | git fetch --prune --unshallow --tags - name: Execute Update .NET script run: | + Import-Module ./.github/workflows/GHWorkflowHelper $currentVersion = (Get-Content .\global.json | ConvertFrom-Json).sdk.version - Write-Verbose "OLD_VERSION=$currentVersion" -Verbose - "OLD_VERSION=$currentVersion" | Out-File $env:GITHUB_ENV -Append + Set-GWVariable -Name OLD_VERSION -Value $currentVersion - ./tools/UpdateDotnetRuntime.ps1 -UpdateMSIPackaging -UseInternalFeed + ./tools/UpdateDotnetRuntime.ps1 -UpdateMSIPackaging $newVersion = (Get-Content .\global.json | ConvertFrom-Json).sdk.version - Write-Verbose "NEW_VERSION=$newVersion" -Verbose - "NEW_VERSION=$newVersion" | Out-File $env:GITHUB_ENV -Append + Set-GWVariable -Name NEW_VERSION -Value $newVersion if ($currentVersion -ne $newVersion) { - Write-Verbose "CREATE_PR=true" -Verbose - "CREATE_PR=true" | Out-File $env:GITHUB_ENV -Append + Set-GWVariable -Name CREATE_PR -Value 'true' } - name: Microsoft Teams Notifier uses: skitionek/notify-microsoft-teams@master if: failure() with: webhook_url: ${{ secrets.PS_BUILD_TEAMS_CHANNEL }} - overwrite: "{title: `Failure in updating .NET build. Look at ${workflow_link}`}" + overwrite: "{title: `Failure in .github/daily.yml updating .NET build. Look at ${workflow_link}`}" - name: Create Pull Request - uses: peter-evans/create-pull-request@v3 + uses: peter-evans/create-pull-request@v4 id: cpr if: env.CREATE_PR == 'true' with: @@ -58,5 +62,93 @@ jobs: title: "Update .NET SDK version from `${{ env.OLD_VERSION }}` to `${{ env.NEW_VERSION }}`" base: master branch: dotnet_update - - + update-tpn: + permissions: + contents: write # for peter-evans/create-pull-request to create branch + pull-requests: write # for peter-evans/create-pull-request to create a PR + name: Update Notices File + timeout-minutes: 15 + runs-on: windows-latest + if: github.repository == 'PowerShell/PowerShell' + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Update Notices file + run: | + Invoke-WebRequest -Uri https://aka.ms/pwsh-daily-tpn -OutFile ./ThirdPartyNotices.txt + - name: Capture Git Status + run: | + git status --short + - name: Check if we need to create a PR + run: | + $ErrorActionPreference = 'continue' + git diff --quiet ThirdPartyNotices.txt + $exitCode = $LASTEXITCODE + Write-Verbose -Message "Exit code: $exitCode" -Verbose + if ($LASTEXITCODE -ne 0) { + Import-Module ./.github/workflows/GHWorkflowHelper + Set-GWVariable -Name CREATE_PR -Value 'true' + } else { + Write-Verbose "No difference found. Not creating a PR." -Verbose + } + exit 0 + - name: Create Pull Request + uses: peter-evans/create-pull-request@v4 + id: cprtpn + if: env.CREATE_PR == 'true' + with: + commit-message: "Update to the latest NOTICES file" + committer: GitHub + author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> + title: "Update to the latest NOTICES file" + reviewers: travisez13 + base: master + draft: false + branch: update-cgmanifest + update-cgmanifest: + permissions: + contents: write # for peter-evans/create-pull-request to create branch + pull-requests: write # for peter-evans/create-pull-request to create a PR + name: Update cgmanifest + timeout-minutes: 15 + runs-on: windows-latest + if: github.repository == 'PowerShell/PowerShell' + env: + CGMANIFEST_PATH: '' + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Sync tags + run: | + git fetch --prune --unshallow --tags + - name: Install Ships provider to deal with project.assets.json + run: | + Install-Module -Name dotnet.project.assets -force + - name: Bootstrap + run: | + Import-Module ./build.psm1 + Start-PSBootStrap + - name: Verify cgmanifest is up to date + run: | + Import-Module ./build.psm1 + Find-Dotnet + ./tools/findMissingNotices.ps1 -Fix + - name: Upload cgmanifest + uses: actions/upload-artifact@v3 + if: always() && env.CGMANIFEST_PATH != '' + with: + name: cgmanifest + path: ${{ env.CGMANIFEST_PATH }} + - name: Create Pull Request + uses: peter-evans/create-pull-request@v4 + id: cprcgmanifest + if: env.CGMANIFEST_PATH != '' + with: + commit-message: "Update the cgmanifest" + committer: GitHub + author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> + title: "Update the cgmanifest" + reviewers: travisez13 + base: master + draft: false + branch: update-cgmanifest diff --git a/.github/workflows/exp-json.yml b/.github/workflows/exp-json.yml new file mode 100644 index 00000000000..629ff0f55e4 --- /dev/null +++ b/.github/workflows/exp-json.yml @@ -0,0 +1,149 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +name: PowerShell Experimental Features Json Update +on: + workflow_dispatch: + schedule: + # At 13:00 UTC every day. + - cron: '0 13 * * *' + +defaults: + run: + shell: pwsh + +env: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + POWERSHELL_TELEMETRY_OPTOUT: 1 + +permissions: + contents: read + +jobs: + create-expjson: + strategy: + matrix: + os: [windows-latest, ubuntu-latest] + timeout-minutes: 15 + runs-on: ${{ matrix.os }} + env: + OS_TITLE: ${{ matrix.os }} + if: github.repository == 'PowerShell/PowerShell' + name: Update experimental features json + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: '0' + - name: Create experimental features file + run: | + Import-Module ./build.psm1 -Force + Start-PSBootstrap + Start-PSBuild -Clean -PSModuleRestore + $builtPwsh = Get-PSOutput + + Write-Verbose -Verbose "PWSH path: $builtPwsh" + + $getExpFeatureJsonScript = @' + [System.Collections.ArrayList] $expFeatures = Get-ExperimentalFeature | Where-Object Name -NE PS7DscSupport | ForEach-Object -MemberName Name + + # Make sure ExperimentalFeatures from modules in PSHome are added + # https://github.com/PowerShell/PowerShell/issues/10550 + $ExperimentalFeaturesFromGalleryModulesInPSHome = @() + $ExperimentalFeaturesFromGalleryModulesInPSHome | ForEach-Object { + if (!$expFeatures.Contains($_)) { + $null = $expFeatures.Add($_) + } + } + + ConvertTo-Json $expFeatures + '@ + + $expFeaturesJson = & $builtPwsh -c $getExpFeatureJsonScript + $osname = $env:OS_TITLE -like 'windows*' ? 'windows' : 'linux' + $fileNamePrefix = "experimental-feature-$osname" + $newFileName = "${fileNamePrefix}-new.json" + + Write-Verbose -Verbose 'Experimental features found' + $expFeaturesJson | Out-String | Write-Verbose -Verbose + $expFeaturesJson | Out-File $newFileName -Force + + - name: Upload experimental features windows + uses: actions/upload-artifact@v3 + with: + name: experimentalJson + path: experimental-feature-*-new.json + + compare-expjson-files: + permissions: + contents: write # for peter-evans/create-pull-request to create branch + pull-requests: write # for peter-evans/create-pull-request to create a PR + runs-on: ubuntu-latest + name: Compare experimental json files and create PR + needs: create-expjson + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: '0' + - name: Download artifacts + uses: actions/download-artifact@v3 + with: + name: experimentalJson + - name: Compare json files + run: | + Import-Module ./.github/workflows/GHWorkflowHelper -Force + + function ShouldCreatePR($currentFile, $newFile) { + if (Test-Path $currentFile) { + $currentExpFeatures = Get-Content $currentFile -Raw | ConvertFrom-Json + $newExpFeatures = Get-Content $newFile -Raw | ConvertFrom-Json + + if (-not (Compare-Object $currentExpFeatures $newExpFeatures)) { + Write-Verbose -Verbose "No changes to experimental features json file" + return $false + } + } + + return $true + } + + $currentWinFile = "experimental-feature-windows.json" + $currentLinuxFile = "experimental-feature-linux.json" + $newWinFile = "experimental-feature-windows-new.json" + $newLinuxFile = "experimental-feature-linux-new.json" + + $createPrWin = ShouldCreatePR $currentWinFile $newWinFile + Write-Verbose -Verbose "Create PR Windows == $createPrWin" + + $createPrLinux = ShouldCreatePR $currentLinuxFile $newLinuxFile + Write-Verbose -Verbose "Create PR Windows == $createPrLinux" + + $createPr = $createPrWin -or $createPrLinux + Write-Verbose -Verbose "Create PR == $createPr" + + if ($createPrWin) { + Move-Item $newWinFile $currentWinFile -Verbose + } + else { + Remove-Item $newWinFile -Verbose + } + + if ($createPrLinux) { + Move-Item $newLinuxFile $currentLinuxFile -Verbose + } + else { + Remove-Item $newLinuxFile -Verbose + } + + Set-GWVariable -Name CREATE_EXP_JSON_PR -Value $createPR + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v4 + id: cpr + if: env.CREATE_EXP_JSON_PR == 'true' + with: + commit-message: "Update experimental-feature-windows.json" + title: "Update experimental-feature json files" + base: master + branch: expjson_update_windows diff --git a/.github/workflows/markdown-link/config.json b/.github/workflows/markdown-link/config.json new file mode 100644 index 00000000000..87d65922a91 --- /dev/null +++ b/.github/workflows/markdown-link/config.json @@ -0,0 +1,7 @@ +{ + "timeout": "40s", + "retryOn429": true, + "retryCount": 5, + "fallbackRetryDelay": "30s", + "aliveStatusCodes": [504, 503, 403, 200] +} diff --git a/.github/workflows/markdownLink.yml b/.github/workflows/markdownLink.yml new file mode 100644 index 00000000000..2c0796ac2d1 --- /dev/null +++ b/.github/workflows/markdownLink.yml @@ -0,0 +1,21 @@ +on: + pull_request: + branches: + - master + - 'release/**' + +name: Check links for modified files +permissions: + contents: read + +jobs: + markdown-link-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: gaurav-nelson/github-action-markdown-link-check@v1 + with: + use-quiet-mode: 'yes' + use-verbose-mode: 'yes' + check-modified-files-only: 'yes' + config-file: .github/workflows/markdown-link/config.json diff --git a/.github/workflows/markdownLinkDaily.yml b/.github/workflows/markdownLinkDaily.yml new file mode 100644 index 00000000000..7a4a1259a64 --- /dev/null +++ b/.github/workflows/markdownLinkDaily.yml @@ -0,0 +1,33 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +name: PowerShell Daily Markdown Link Verification + +on: + workflow_dispatch: + schedule: + # At 13:00 UTC every day. + - cron: '0 13 * * *' + +permissions: + contents: read + +jobs: + markdown-link-check: + runs-on: ubuntu-latest + if: github.repository == 'PowerShell/PowerShell' + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Check Links + uses: gaurav-nelson/github-action-markdown-link-check@v1 + with: + use-quiet-mode: 'yes' + use-verbose-mode: 'yes' + config-file: .github/workflows/markdown-link/config.json + - name: Microsoft Teams Notifier + uses: skitionek/notify-microsoft-teams@master + if: failure() + with: + webhook_url: ${{ secrets.PS_BUILD_TEAMS_CHANNEL }} + overwrite: "{title: `Failure in .github/markdownLinkDaily.yml validating links. Look at ${workflow_link}`}" diff --git a/.github/workflows/processReminders.yml b/.github/workflows/processReminders.yml new file mode 100644 index 00000000000..c660788a620 --- /dev/null +++ b/.github/workflows/processReminders.yml @@ -0,0 +1,20 @@ +name: 'Process reminders' + +on: + schedule: + - cron: '*/15 * * * *' + workflow_dispatch: + +permissions: + contents: read + +jobs: + reminder: + permissions: + issues: write # for agrc/reminder-action to set reminders on issues + pull-requests: write # for agrc/reminder-action to set reminders on PRs + runs-on: ubuntu-latest + + steps: + - name: check reminders and notify + uses: agrc/reminder-action@v1 diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml index f54254f1435..2ec027625b6 100644 --- a/.github/workflows/rebase.yml +++ b/.github/workflows/rebase.yml @@ -1,21 +1,38 @@ # This cannot rebase workflow changes into a PR # It also only works if the GITHUB_TOKEN has permission to push to the branch # see: https://github.com/cirrus-actions/rebase/issues/12#issuecomment-632594995 -on: +on: issue_comment: types: [created] name: Automatic Rebase +permissions: + contents: read + jobs: rebase: + permissions: + contents: write # for cirrus-actions/rebase to push code to rebase + pull-requests: write # for actions/github-script to create PR comment name: Rebase if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') runs-on: ubuntu-latest steps: - name: Checkout the latest code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 + - name: Post rebase started comment to pull request + uses: actions/github-script@v6 + with: + script: | + const backport_start_body = `Started rebase: https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}`; + await github.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: backport_start_body + }); - name: Automatic Rebase - uses: cirrus-actions/rebase@1.4 + uses: cirrus-actions/rebase@1.7 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.poshchan/settings.json b/.poshchan/settings.json deleted file mode 100644 index 21ad0f08b48..00000000000 --- a/.poshchan/settings.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "version": "0.1", - "azdevops": { - "build_targets": { - "static": "PowerShell-CI-static-analysis", - "windows": "PowerShell-CI-Windows", - "macos": "PowerShell-CI-macOS", - "linux": "PowerShell-CI-Linux", - "ssh": "PowerShell-CI-SSH", - "all": [ - "PowerShell-CI-static-analysis", - "PowerShell-CI-Windows", - "PowerShell-CI-macOS", - "PowerShell-CI-Linux", - "PowerShell-CI-SSH" - ] - }, - "authorized_users": [ - "adityapatwardhan", - "anmenaga", - "bergmeister", - "daxian-dbw", - "iSazonov", - "JamesWTruher", - "KirkMunro", - "PaulHigin", - "rjmholt", - "SteveL-MSFT", - "TravisEz13", - "TylerLeonhardt", - "vexx32" - ] - }, - "failures": { - "authorized_users": [ - "adityapatwardhan", - "anmenaga", - "bergmeister", - "daxian-dbw", - "IISResetMe", - "iSazonov", - "JamesWTruher", - "KirkMunro", - "kwkam", - "PaulHigin", - "powercode", - "rjmholt", - "rkeithhill", - "SteveL-MSFT", - "TravisEz13", - "TylerLeonhardt", - "vexx32" - ] - }, - "reminders": { - "authorized_users": "*" - } -} diff --git a/.spelling b/.spelling index c62416ad9e4..7c964a07675 100644 --- a/.spelling +++ b/.spelling @@ -8,13 +8,16 @@ 0xfeeddeadbeef 100ms 1redone +1.final 2.x 2ae5d07 32-bit +4.final 64-bit about_ about_debuggers about_jobs +about_Telemetry acl adamdriscoll add-localgroupmember @@ -27,6 +30,7 @@ adityapatwardhan ADOPTERS.md aetos382 aiello +al-cheb alepauly alexandair alexjordan6 @@ -45,6 +49,7 @@ alpha.9 alternatestream alvarodelvalle amd64 +ananya26-vishnoi andschwa anmenaga api @@ -53,6 +58,7 @@ APIScan appimage applocker appveyor +AppX args argumentlist arm32 @@ -70,6 +76,7 @@ azurerm.profile.netcore.preview azurerm.resources.netcore.preview backgrounded backgrounding +backport beatcracker bergmeister beta.1 @@ -84,7 +91,9 @@ beta.9 beta2 bgelens Bhaal22 +BinaryFormatter bjh7242 +bnot bool bpayette brcrista @@ -96,25 +105,33 @@ bugfix build.json build.psm1 bulid +buildInfoJson callmejoebob catchable cdxml celsius CentOS +codeql-action +cgmanifest +CGManifest changelog changelog.md changelogs changeset changesets channel9 +charltonstanley charset checkbox checksum chibi childitem +ChuckieChen945 ChrisLGardner cimsession cimsupport +ci.psm1 +cgmanifest classlib clear-itemproperty cloudydino @@ -136,7 +153,9 @@ CommandInvocationIntrinsics commandsearch CommandSearcher comobject +Compiler.cs composability +ComRuntimeHelpers.cs config connect-pssession consolehost @@ -216,6 +235,7 @@ displaydataquery Distribution_Request.md distro distros +dkaszews dll dlls dlwyatt @@ -229,10 +249,13 @@ dongbo dotcover dotnet dotnetcore +dotnetmetadata.json DotnetRutimeMetadata.json +DotnetRuntimeMetadata.json dottedscopes downlevel dropdown +dwtaber e.g. ebook ebooks @@ -256,6 +279,7 @@ ergo3114 errorrecord etl eugenesmlv +EventLogLogProvider excludeversion exe executables @@ -269,18 +293,22 @@ export-formatdata export-modulemember failurecode failurecount +farmerau fbehrens felixfbecker ffeldhaus ffi +fflaten filecatalog filename filesystem filesystemprovider +files.wxs filterhashtable find-dscresource find-packageprovider find-rolecapability +findMissingNotices.ps1 firefox folderName foreach @@ -335,6 +363,7 @@ get-typedata get-uiculture get-winevent get-wsmaninstance +Get-WSManSupport GetExceptionForHR getparentprocess gettype @@ -352,6 +381,7 @@ hackathons HashSet hashtable hashtables +hayhay27 helloworld.ps1 helpproviderwithcache helpproviderwithfullcache @@ -362,6 +392,7 @@ Higinbotham himura2la hololens homebrew +hostifaces hostname hotfix httpbin.org @@ -369,6 +400,7 @@ httpbin's https hubuk hvitved +i3arnon i.e. ico idera @@ -426,8 +458,10 @@ jwmoss kanjibates kasper3 katacoda +Kellen-Stuart kevinmarquette kevinoid +KevRitchie keyfileparameter keyhandler khansen00 @@ -465,6 +499,7 @@ lukexjeremy lupino3 lynda.com lzybkr +m1k0net M1kep mababio macos @@ -472,10 +507,13 @@ macports maertendmsft mahawar Markdig.Signed +markdown.yml markekraus marktiedemann Marusyk MarvTheRobot +mattifestation +matt9ucci mcbobke md meir017 @@ -485,15 +523,19 @@ messageanalyzer metadata metadata.json miaromero +michaeltlombardi microsoft Microsoft.ApplicationInsights Microsoft.CodeAnalysis.CSharp Microsoft.CodeAnalysis.NetAnalyzers microsoft.com +Microsoft.Management microsoft.management.infrastructure.cimcmdlets microsoft.management.infrastructure.native +Microsoft.Management.Infrastructure.Runtime.Win microsoft.net.test.sdk microsoft.powershell.archive +Microsoft.PowerShell.Commands microsoft.powershell.commands.diagnostics microsoft.powershell.commands.management microsoft.powershell.commands.utility @@ -508,6 +550,8 @@ microsoft.powershell.markdownrender microsoft.powershell.psreadline microsoft.powershell.security microsoft.powershell.utility +Microsoft.Security.Extensions +Microsoft.WSMan microsoft.wsman.management microsoft.wsman.runtime mikeTWC1984 @@ -515,7 +559,9 @@ mirichmo mjanko5 mkdir mklement0 +ModuleCmdletBase.cs MohiTheFish +Molkree move-itemproperty ms-psrp msbuild @@ -531,6 +577,7 @@ mwrock myget namedpipe nameof +NameObscurerTelemetryInitializer namespace nano nanoserver @@ -561,6 +608,7 @@ new-winevent new-wsmaninstance new-wsmansessionoption NextTurn +ngharo NJsonSchema NoMoreFood non-22 @@ -593,6 +641,7 @@ openssh openssl opensuse oss +OutputType p1 packagemanagement PackageVersion @@ -600,8 +649,11 @@ parameshbabu parameterbinderbase parameterbindercontroller parameterbinding +ParenExpression ParseError.ToString +Path.Join pathresolution +PathResolvedToMultiple patochun patwardhan paulhigin @@ -610,6 +662,7 @@ payette perf perfview perfview.exe +peter-evans petseral plaintext pluggable @@ -631,12 +684,14 @@ powershellgallery powershellget powershellmagazine.com powershellninja +powershellpr0mpt powershellproperties ppadmavilasom pre-build pre-compiled pre-generated pre-installed +pre-parse pre-release pre-releases pre-requisites @@ -644,6 +699,7 @@ prepend preprocessor preview.1 preview.2 +preview.2.22153.17 preview.3 preview.4 preview.5 @@ -652,6 +708,7 @@ preview.5.20278.13 preview.5.20269.29 preview.5.20268.9 preview.5.20272.6 +preview.5.22307.18 preview.6 preview.6.20318.15 preview.6.21355.2 @@ -661,11 +718,15 @@ preview.7.20358.6 preview.7.20364.3 preview.7.20366.2 preview.7.20366.15 +preview.7.22377.5 preview.4.20258.7 preview.4.20229.10 +preview.4.22252.9 preview.8 preview1-24530-04 +preview1.22217.1 preview7 +ProcessorArchitecture productversion program.cs prototyyppi @@ -699,6 +760,7 @@ pvs-studio pwd pwrshplughin.dll pwsh +pwsh.deps.json qmfrederik raghav710 RandomNoun7 @@ -708,6 +770,7 @@ rc.1 rc.1.21455.2 rc.1.21458.32 rc.2 +rc.2.22477.20 rc.3 rc2-24027 rc3-24011 @@ -717,11 +780,13 @@ readme.md readonly ReadyToRun rebase +rebase.yml rebasing receive-pssession recurse reddit redhat +redirections redistributable redistributables register-argumentcompleter @@ -769,11 +834,13 @@ rkitover robo210 ronn rpalo +rpolley runspace runspaceinit runspaces runtime runtimes +Ryan-Hutchison-USAF SA1026CodeMustNotContainSpaceAfterNewKeywordInImplicitlyTypedArrayAllocation Saancreed sample-dotnet1 @@ -789,6 +856,7 @@ scriptblock securestring seemethere select-xml +SemanticChecks semver serverless sessionid @@ -811,6 +879,7 @@ set-wsmaninstance set-wsmanquickconfig sethvs setversionvariables +sha256 ShaydeNofziger shellexecute shouldbeerrorid @@ -819,6 +888,7 @@ Shriram0908 silijon simonwahlin singleline +sles15 smes snapcraft snapin @@ -836,6 +906,7 @@ start-codecoveragerun start-pspester stdin stevel-msft +StevenLiekens stevend811 stknohg strawgate @@ -845,6 +916,7 @@ string.split stringbuilder stuntguy3000 StyleCop +subfolder submodule submodules sudo @@ -862,6 +934,7 @@ System.IO.Packaging System.InvalidOperationException system.manage system.management.automation +System.Management.Automation.utils systemd SytzeAndr tabcompletion @@ -906,6 +979,7 @@ tracesource travisez13 travisty truher +TSAUpload TValue tylerleonhardt typecataloggen @@ -914,6 +988,7 @@ typegen typematch t_ ubuntu +ubuntu22.04 uint un-versioned unicode @@ -967,11 +1042,19 @@ v6.2.4 v7.0.0 v7.0.3 v7.0.4 +v7.0.9 v7.1.0 +v7.1.6 +v7.1.7 +v7.2.2 +v7.3.0 validatenotnullorempty +ValidateSet +varunsh-coder versioned versioning vexx32 +Virtualization visualstudio vmsilvamolina vorobev @@ -993,6 +1076,7 @@ wildcards win32 win32-openssh win7 +win8 windos windowspsmodulepath windowsversion @@ -1020,8 +1104,10 @@ yecril71pl yml youtube Youssef1313 +Yulv-git zackjknight ComInterop +ryneandal runtime#33060 vexx32 perf @@ -1050,6 +1136,7 @@ unvalidated Geweldig mjanko5 v7.0.0 +v7.0.10 renehernandez ece-jacob-scott st0le @@ -1126,6 +1213,31 @@ Microsoft.Management.UI.Internal StringComparison osx-arm64 crossgen2 +MartinGC94 +BrannenGH +SergeyZalyadeev +KiwiThePoodle +Thomas-Yu +cgmanifest.json +mcr.microsoft.com +global.json +tar.gz +psoptions.json +manifest.spdx.json +buildinfo +SKUs +vmImage +InternalCommands.cs +CommonCommandParameters.cs +preview.6.22352.1 +v2.2.6 +ResultsComparer +pre-defined +System.Runtime.CompilerServices.Unsafe +TabExpansion +PSv2 +System.Data.SqlClient +v7.3.3 - CHANGELOG.md aavdberg asrosent @@ -1269,7 +1381,8 @@ package.json jcotton42 RPMs PSDesiredStateConfiguration - - CHANGELOG/preview.md +dotnet5 + - CHANGELOG/7.2.md Gimly jborean93 mkswd @@ -1383,15 +1496,99 @@ lselden SethFalco CodeQL slowy07 +rc.2.21505.57 +ThirdPartyNotices +ThirdPartyNotices.txt +cgmanifest.json +buildinfo +tar.gz +psoptions.json +manifest.spdx.json +vPack +kondratyev-nv +v7.2.0 +v7.2.3 +cgmanifest.json +pwsh.exe +6.0.100-rtm.21527.11 +6.0.100-rc.2.21505.57 +ThirdPartyNotices.txt +rtm.21527.11 +SKUs +vmImage +Ubuntu22.04 - CHANGELOG/7.0.md codesign release-BuildJson yml +dotnet5 +buildinfo +SKUs +CGManifest +vmImage +ci.psm1 centos-7 PSDesiredStateConfiguration +NoLanguage +createdump +vPack +PkgES - test/perf/benchmarks/README.md benchmarked BenchmarkDotNet - docs/community/working-group-definitions.md -TobiasPSP +gaelcolas jdhitsolutions +jhoneill +kilasuit +michaeltlombardi +SeeminglyScience +TobiasPSP + - CHANGELOG/7.3.md +ayousuf23 +AzCopy.exe +hammy3502 +PowerShellExecutionHelper.cs +ClientRemotePowerShell +DOTNET_ROOT +SkipExperimentalFeatureGeneration +AzCopy +Start-PSBootStrap +precheck +SKUs +powershell.config.json +Microsoft.PowerShell.GlobalTool.Shim.csproj +InvokeCommand +UseDotNet +vmImage +NoLanguage +GetValueOrDefault +kondratyev-nv +penimc_cor3.dll +PkgES +v7.2.0 +preview.9 +pwsh.exe +XunitXml.TestLogger +rtm.21527.11 +ThirdPartyNotices.txt +buildinfo +tar.gz +rc.2.21505.57 +psoptions.json +manifest.spdx.json +AzureFileCopy +vPack +dotnet5 +buildinfo +SKUs +CGManifest +vmImage +ci.psm1 +jcotton42 +centos-7 +Security.types.ps1xml + - ADOPTERS.md +MicrosoftPowerBIMgmt + - tools/clearlyDefined/readme.md +ClearlyDefined diff --git a/.vsts-ci/linux-daily.yml b/.vsts-ci/linux-daily.yml index e6a5375b0c9..82705e8b5ce 100644 --- a/.vsts-ci/linux-daily.yml +++ b/.vsts-ci/linux-daily.yml @@ -18,17 +18,9 @@ pr: branches: include: - master - - release* - - feature* paths: include: - - '*' - exclude: - - tools/releaseBuild/* - - tools/releaseBuild/azureDevOps/templates/* - - /.vsts-ci/misc-analysis.yml - - /.github/ISSUE_TEMPLATE/* - - /.dependabot/config.yml + - .vsts-ci/linux-daily.yml variables: DOTNET_CLI_TELEMETRY_OPTOUT: 1 @@ -55,6 +47,7 @@ stages: displayName: Test for Linux jobs: - job: linux_test + timeoutInMinutes: 90 pool: vmImage: ubuntu-20.04 displayName: Linux Test diff --git a/.vsts-ci/linux.yml b/.vsts-ci/linux.yml index 0cc59284859..6a3f0280d0c 100644 --- a/.vsts-ci/linux.yml +++ b/.vsts-ci/linux.yml @@ -1,3 +1,12 @@ +parameters: + - name: ContainerPattern + displayName: | + Pattern to match JobName of the container. + Update this to force a container. + `.` will match everything + type: string + default: . + name: PR-$(System.PullRequest.PullRequestNumber)-$(Date:yyyyMMdd)$(Rev:.rr) trigger: # Batch merge builds together while a merge build is running @@ -11,9 +20,9 @@ trigger: include: - '*' exclude: - - /.vsts-ci/misc-analysis.yml - - /.github/ISSUE_TEMPLATE/* - - /.dependabot/config.yml + - .vsts-ci/misc-analysis.yml + - .github/ISSUE_TEMPLATE/* + - .dependabot/config.yml - test/perf/* pr: branches: @@ -30,10 +39,14 @@ pr: - .vsts-ci/misc-analysis.yml - .vsts-ci/windows.yml - .vsts-ci/windows/* + - tools/cgmanifest.json + - LICENSE.txt - test/common/markdown/* - test/perf/* - tools/releaseBuild/* - tools/releaseBuild/azureDevOps/templates/* + - README.md + - .spelling variables: DOTNET_CLI_TELEMETRY_OPTOUT: 1 @@ -43,11 +56,15 @@ variables: __SuppressAnsiEscapeSequences: 1 resources: -- repo: self - clean: true + repositories: + - repository: Docker + type: github + endpoint: PowerShell + name: PowerShell/PowerShell-Docker + ref: master stages: -- stage: BuildLinux +- stage: BuildLinuxStage displayName: Build for Linux jobs: - template: templates/ci-build.yml @@ -56,33 +73,34 @@ stages: jobName: linux_build displayName: linux Build -- stage: TestLinux - displayName: Test for Linux +- stage: TestUbuntu + displayName: Test for Ubuntu + dependsOn: [BuildLinuxStage] jobs: - template: templates/nix-test.yml parameters: - name: Linux + name: Ubuntu pool: ubuntu-20.04 purpose: UnelevatedPesterTests tagSet: CI - template: templates/nix-test.yml parameters: - name: Linux + name: Ubuntu pool: ubuntu-20.04 purpose: ElevatedPesterTests tagSet: CI - template: templates/nix-test.yml parameters: - name: Linux + name: Ubuntu pool: ubuntu-20.04 purpose: UnelevatedPesterTests tagSet: Others - template: templates/nix-test.yml parameters: - name: Linux + name: Ubuntu pool: ubuntu-20.04 purpose: ElevatedPesterTests tagSet: Others @@ -91,24 +109,61 @@ stages: parameters: pool: ubuntu-20.04 +- stage: TestContainer + displayName: Test in a container + dependsOn: [BuildLinuxStage] + jobs: + - job: getContainerJob + displayName: Choose a container + pool: + vmImage: ubuntu-20.04 + steps: + - checkout: self + clean: true + + - checkout: Docker + clean: true + + - pwsh: | + # Initialize container test stage + Import-Module ./PowerShell/tools/ci.psm1 + Invoke-InitializeContainerStage -ContainerPattern '${{ parameters.ContainerPattern }}' + name: getContainerTask + displayName: Initialize Container Stage + continueOnError: true + + - template: templates/test/nix-container-test.yml + parameters: + name: container + pool: ubuntu-20.04 + purpose: UnelevatedPesterTests + tagSet: CI + + - template: templates/test/nix-container-test.yml + parameters: + name: container + pool: ubuntu-20.04 + purpose: ElevatedPesterTests + tagSet: CI + + - template: templates/test/nix-container-test.yml + parameters: + name: container + pool: ubuntu-20.04 + purpose: UnelevatedPesterTests + tagSet: Others + + - template: templates/test/nix-container-test.yml + parameters: + name: container + pool: ubuntu-20.04 + purpose: ElevatedPesterTests + tagSet: Others + - stage: PackageLinux displayName: Package Linux - dependsOn: ["BuildLinux"] + dependsOn: ["BuildLinuxStage"] jobs: - template: linux/templates/packaging.yml parameters: pool: ubuntu-20.04 - -- stage: CodeCovTestPackage - displayName: CodeCoverage and Test Packages - dependsOn: [] # by specifying an empty array, this stage doesn't depend on the stage before it - jobs: - - job: CodeCovTestPackage - displayName: CodeCoverage and Test Packages - pool: - vmImage: ubuntu-20.04 - steps: - - pwsh: | - Import-Module .\tools\ci.psm1 - New-CodeCoverageAndTestPackage - displayName: CodeCoverage and Test Package diff --git a/.vsts-ci/mac.yml b/.vsts-ci/mac.yml index 3cd35335bef..24646a0da4b 100644 --- a/.vsts-ci/mac.yml +++ b/.vsts-ci/mac.yml @@ -11,10 +11,10 @@ trigger: include: - '*' exclude: - - /tools/releaseBuild/**/* - - /.vsts-ci/misc-analysis.yml - - /.github/ISSUE_TEMPLATE/* - - /.dependabot/config.yml + - tools/releaseBuild/**/* + - .vsts-ci/misc-analysis.yml + - .github/ISSUE_TEMPLATE/* + - .dependabot/config.yml - test/perf/* pr: branches: @@ -29,13 +29,17 @@ pr: - .dependabot/config.yml - .github/ISSUE_TEMPLATE/* - .vsts-ci/misc-analysis.yml - - /.vsts-ci/windows.yml - - /.vsts-ci/windows/* + - .vsts-ci/windows.yml + - .vsts-ci/windows/* + - tools/cgmanifest.json + - LICENSE.txt - test/common/markdown/* - test/perf/* - tools/packaging/* - tools/releaseBuild/* - tools/releaseBuild/azureDevOps/templates/* + - README.md + - .spelling variables: DOTNET_CLI_TELEMETRY_OPTOUT: 1 @@ -87,20 +91,3 @@ stages: parameters: pool: macOS-latest -- stage: CodeCovTestPackage - displayName: CodeCoverage and Test Packages - dependsOn: [] # by specifying an empty array, this stage doesn't depend on the stage before it - jobs: - - job: CodeCovTestPackage - displayName: CodeCoverage and Test Packages - pool: - vmImage: macOS-latest - steps: - - pwsh: | - # Remove old .NET SDKs - if (Test-Path -Path $HOME/.dotnet) { - Remove-Item $HOME/.dotnet -Recurse -Force - } - Import-Module .\tools\ci.psm1 - New-CodeCoverageAndTestPackage - displayName: CodeCoverage and Test Package diff --git a/.vsts-ci/misc-analysis.yml b/.vsts-ci/misc-analysis.yml index 5de5e28265a..b9e19a2a505 100644 --- a/.vsts-ci/misc-analysis.yml +++ b/.vsts-ci/misc-analysis.yml @@ -5,14 +5,12 @@ trigger: branches: include: - master - - release* - feature* pr: branches: include: - master - - release* - feature* resources: @@ -27,83 +25,29 @@ variables: - name: repoFolder value: PowerShell -jobs: -- job: CI_Compliance - displayName: CI Compliance +stages: +- stage: Compliance + jobs: + - job: CI_Compliance + displayName: CI Compliance - pool: - vmImage: windows-latest + pool: + vmImage: windows-latest - variables: - - name: repoPath - value: $(Agent.BuildDirectory)\$(repoFolder) + variables: + - name: repoPath + value: $(Agent.BuildDirectory)\$(repoFolder) - steps: - - checkout: self - clean: true - path: $(repoFolder) + steps: + - checkout: self + clean: true + path: $(repoFolder) - - checkout: ComplianceRepo + - checkout: ComplianceRepo - - template: ci-compliance.yml@ComplianceRepo - -- job: Linux_CI - displayName: Markdown and Common Tests - - pool: - vmImage: ubuntu-20.04 - - variables: - - name: repoPath - value: $(Agent.BuildDirectory)/$(repoFolder) - - steps: - - checkout: self - clean: true - path: $(repoFolder) - - - checkout: ComplianceRepo - - - powershell: | - Get-ChildItem -Path env: - displayName: Capture Environment - condition: succeededOrFailed() - - - powershell: | - Install-module Pester -Scope CurrentUser -Force -MaximumVersion 4.99 - displayName: Install Pester - condition: succeededOrFailed() - - - bash: | - curl -o- --progress-bar -L https://yarnpkg.com/install.sh | bash - displayName: Bootstrap Yarn - condition: succeededOrFailed() - - - bash: | - sudo yarn global add markdown-spellcheck@0.11.0 - displayName: Install mdspell - condition: succeededOrFailed() - - - bash: | - mdspell '**/*.md' '!**/Pester/**/*.md' '!**/dotnet-tools/**/*.md' --ignore-numbers --ignore-acronyms --report --en-us; - displayName: Test Spelling in Markdown - condition: succeededOrFailed() - workingDirectory: '$(repoPath)' - - - ${{ if not(contains(variables['SYSTEM.COLLECTIONURI'],'mscodehub')) }}: - - pwsh: | - Import-module ./build.psm1 - $path = Join-Path -Path $pwd -ChildPath './commonTestResults.xml' - $results = invoke-pester -Script ./test/common -OutputFile $path -OutputFormat NUnitXml -PassThru - Write-Host "##vso[results.publish type=NUnit;mergeResults=true;runTitle=Common Tests;publishRunAttachments=true;resultFiles=$path;]" - if($results.TotalCount -eq 0 -or $results.FailedCount -gt 0) - { - throw "Markdown tests failed" - } - displayName: Run Common Tests - condition: succeededOrFailed() - workingDirectory: '$(repoPath)' - - - template: dailyBuildCompliance.yml@ComplianceRepo - parameters: - sourceScanPath: '$(repoPath)' + - template: ci-compliance.yml@ComplianceRepo +- stage: markdown_spelling_lint + displayName: Markdown Spelling and Lint + dependsOn: [] + jobs: + - template: ./misc-analysis/mdSpell.yml diff --git a/.vsts-ci/misc-analysis/generateMarkdownMatrix.yml b/.vsts-ci/misc-analysis/generateMarkdownMatrix.yml new file mode 100644 index 00000000000..56a43accd55 --- /dev/null +++ b/.vsts-ci/misc-analysis/generateMarkdownMatrix.yml @@ -0,0 +1,46 @@ +parameters: + - name: jobName + - name: taskName + +jobs: +- job: ${{ parameters.jobName }} + displayName: Generate Markdown Matrix + + pool: + vmImage: ubuntu-20.04 + + variables: + - name: repoPath + value: $(Agent.BuildDirectory)/$(repoFolder) + + steps: + - checkout: self + clean: true + path: $(repoFolder) + + - powershell: | + $matrix = @{} + $matrix += @{ + 'root' = @{ + markdown_folder = "$(repoPath)" + markdown_recurse = $false + } + } + Get-ChildItem -path '$(repoPath)' -Directory | Foreach-Object { + $folder = $_ + $matrix += @{ + $_.Name = @{ + markdown_folder = $_.fullName + markdown_recurse = $true + } + } + } + + $matrixJson = $matrix | ConvertTo-Json -Compress + $variableName = "matrix" + $command = "vso[task.setvariable variable=$variableName;isoutput=true]$($matrixJson)" + Write-Verbose "sending command: '$command'" + Write-Host "##$command" + displayName: Create Matrix + condition: succeededOrFailed() + name: ${{ parameters.taskName }} diff --git a/.vsts-ci/misc-analysis/markdown.yml b/.vsts-ci/misc-analysis/markdown.yml new file mode 100644 index 00000000000..b63e817ef35 --- /dev/null +++ b/.vsts-ci/misc-analysis/markdown.yml @@ -0,0 +1,57 @@ +parameters: + - name: matrix + - name: dependsOn + +jobs: +- job: markdown + strategy: + matrix: ${{ parameters.matrix }} + maxParallel: 5 + + displayName: Markdown Link Verification + + dependsOn: ${{ parameters.dependsOn }} + + pool: + vmImage: ubuntu-20.04 + + variables: + - name: repoPath + value: $(Agent.BuildDirectory)/$(repoFolder) + - name: YARN_CACHE_FOLDER + value: $(Pipeline.Workspace)/.yarn + - name: YARN_GLOBAL_CACHE_FOLDER + value: $(Pipeline.Workspace)/.yarn-global + + steps: + - checkout: self + clean: true + path: $(repoFolder) + + - checkout: ComplianceRepo + + - powershell: | + Install-module Pester -Scope CurrentUser -Force -MaximumVersion 4.99 + displayName: Install Pester + + - bash: | + curl -o- --progress-bar -L https://yarnpkg.com/install.sh | bash + displayName: Bootstrap Yarn + + - bash: | + yarn config set global-folder "$(YARN_GLOBAL_CACHE_FOLDER)" + displayName: Set Yarn global cache folder + + - ${{ if not(contains(variables['SYSTEM.COLLECTIONURI'],'mscodehub')) }}: + - pwsh: | + Import-module ./build.psm1 + $path = Join-Path -Path $pwd -ChildPath './commonTestResults.xml' + $results = invoke-pester -Script ./test/common/markdown-link -OutputFile $path -OutputFormat NUnitXml -PassThru + Write-Host "##vso[results.publish type=NUnit;mergeResults=true;runTitle=Markdown Link;publishRunAttachments=true;resultFiles=$path;]" + if($results.TotalCount -eq 0 -or $results.FailedCount -gt 0) + { + throw "Markdown tests failed" + } + displayName: Run Markdown Link Tests + condition: succeededOrFailed() + workingDirectory: '$(repoPath)' diff --git a/.vsts-ci/misc-analysis/mdSpell.yml b/.vsts-ci/misc-analysis/mdSpell.yml new file mode 100644 index 00000000000..e9d046e5e96 --- /dev/null +++ b/.vsts-ci/misc-analysis/mdSpell.yml @@ -0,0 +1,56 @@ +jobs: +- job: markdown + displayName: Markdown Spelling + + pool: + vmImage: ubuntu-20.04 + + variables: + - name: repoPath + value: $(Agent.BuildDirectory)/$(repoFolder) + + steps: + - checkout: self + clean: true + path: $(repoFolder) + + - checkout: ComplianceRepo + + - powershell: | + Get-ChildItem -Path env: + displayName: Capture Environment + condition: succeededOrFailed() + + - bash: | + curl -o- --progress-bar -L https://yarnpkg.com/install.sh | bash + displayName: Bootstrap Yarn + condition: succeededOrFailed() + + - bash: | + sudo yarn global add markdown-spellcheck@0.11.0 + displayName: Install mdspell + condition: succeededOrFailed() + + - bash: | + mdspell '**/*.md' '!**/Pester/**/*.md' '!**/dotnet-tools/**/*.md' --ignore-numbers --ignore-acronyms --report --en-us; + displayName: Test Spelling in Markdown + condition: succeededOrFailed() + workingDirectory: '$(repoPath)' + + - ${{ if not(contains(variables['SYSTEM.COLLECTIONURI'],'mscodehub')) }}: + - pwsh: | + Import-module ./build.psm1 + $path = Join-Path -Path $pwd -ChildPath './commonTestResults.xml' + $results = invoke-pester -Script ./test/common/markdown-lint -OutputFile $path -OutputFormat NUnitXml -PassThru + Write-Host "##vso[results.publish type=NUnit;mergeResults=true;runTitle=Markdown Lint;publishRunAttachments=true;resultFiles=$path;]" + if($results.TotalCount -eq 0 -or $results.FailedCount -gt 0) + { + throw "Markdown tests failed" + } + displayName: Run Markdown Lint Tests + condition: succeededOrFailed() + workingDirectory: '$(repoPath)' + + - template: dailyBuildCompliance.yml@ComplianceRepo + parameters: + sourceScanPath: '$(repoPath)/test/common' diff --git a/.vsts-ci/sshremoting-tests.yml b/.vsts-ci/sshremoting-tests.yml index 016f3bfddca..e7b7003c3b7 100644 --- a/.vsts-ci/sshremoting-tests.yml +++ b/.vsts-ci/sshremoting-tests.yml @@ -23,17 +23,29 @@ pr: - '/test/SSHRemoting/*' variables: - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - POWERSHELL_TELEMETRY_OPTOUT: 1 + - name: DOTNET_CLI_TELEMETRY_OPTOUT + value: 1 + - name: POWERSHELL_TELEMETRY_OPTOUT + value: 1 # Avoid expensive initialization of dotnet cli, see: https://donovanbrown.com/post/Stop-wasting-time-during-NET-Core-builds - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 - __SuppressAnsiEscapeSequences: 1 + - name: DOTNET_SKIP_FIRST_TIME_EXPERIENCE + value: 1 + - name: __SuppressAnsiEscapeSequences + value: 1 + - name: NugetSecurityAnalysisWarningLevel + value: none +# Prevents auto-injection of nuget-security-analysis@0 + - name: skipNugetSecurityAnalysis + value: true + resources: - repo: self clean: true jobs: - job: SSHRemotingTests + pool: + vmImage: ubuntu-20.04 container: mcr.microsoft.com/powershell/test-deps:ubuntu-18.04 displayName: SSH Remoting Tests diff --git a/.vsts-ci/templates/ci-build.yml b/.vsts-ci/templates/ci-build.yml index b88aa1b7c3d..e64ab4967a2 100644 --- a/.vsts-ci/templates/ci-build.yml +++ b/.vsts-ci/templates/ci-build.yml @@ -1,5 +1,5 @@ parameters: - pool: 'vs2017-win2016' + pool: 'windows-latest' jobName: 'win_build' displayName: Windows Build @@ -11,38 +11,31 @@ jobs: displayName: ${{ parameters.displayName }} steps: - - powershell: | + - pwsh: | Get-ChildItem -Path env: displayName: Capture Environment condition: succeededOrFailed() - - powershell: Write-Host "##vso[build.updatebuildnumber]$env:BUILD_SOURCEBRANCHNAME-$env:BUILD_SOURCEVERSION-$((get-date).ToString("yyyyMMddhhmmss"))" + - pwsh: Write-Host "##vso[build.updatebuildnumber]$env:BUILD_SOURCEBRANCHNAME-$env:BUILD_SOURCEVERSION-$((get-date).ToString("yyyyMMddhhmmss"))" displayName: Set Build Name for Non-PR condition: ne(variables['Build.Reason'], 'PullRequest') - ${{ if ne(variables['AzDevOpsFeed'], '') }}: - template: /tools/releaseBuild/azureDevOps/templates/insert-nuget-config-azfeed.yml - - pwsh: | - if (Test-Path -Path $HOME/.dotnet) { - Remove-Item $HOME/.dotnet -Recurse -Force - } - displayName: Remove Old .NET SDKs - condition: succeededOrFailed() - - pwsh: | Import-Module .\tools\ci.psm1 Invoke-CIInstall -SkipUser displayName: Bootstrap condition: succeeded() - - powershell: | + - pwsh: | Import-Module .\tools\ci.psm1 Invoke-CIBuild displayName: Build condition: succeeded() - - powershell: | + - pwsh: | Import-Module .\tools\ci.psm1 Restore-PSOptions Invoke-CIxUnit -SkipFailing diff --git a/.vsts-ci/templates/credscan.yml b/.vsts-ci/templates/credscan.yml index f6ed5b8fd23..60094ff3d77 100644 --- a/.vsts-ci/templates/credscan.yml +++ b/.vsts-ci/templates/credscan.yml @@ -1,12 +1,12 @@ parameters: - pool: 'Hosted VS2017' + pool: 'windows-latest' jobName: 'credscan' displayName: Secret Scan jobs: - job: ${{ parameters.jobName }} pool: - name: ${{ parameters.pool }} + vmImage: ${{ parameters.pool }} displayName: ${{ parameters.displayName }} diff --git a/.vsts-ci/templates/nanoserver.yml b/.vsts-ci/templates/nanoserver.yml index c989d01c2f8..ae9f639b3b2 100644 --- a/.vsts-ci/templates/nanoserver.yml +++ b/.vsts-ci/templates/nanoserver.yml @@ -1,5 +1,5 @@ parameters: - vmImage: 'windows-2019' + vmImage: 'windows-latest' jobName: 'Nanoserver_Tests' continueOnError: false diff --git a/.vsts-ci/templates/nix-test.yml b/.vsts-ci/templates/nix-test.yml index 6a1b6e6f9de..ab3985dacd6 100644 --- a/.vsts-ci/templates/nix-test.yml +++ b/.vsts-ci/templates/nix-test.yml @@ -1,78 +1,19 @@ parameters: pool: 'macOS-latest' - parentJobs: [] purpose: '' tagSet: 'CI' name: 'mac' jobs: - job: ${{ parameters.name }}_test_${{ parameters.purpose }}_${{ parameters.tagSet }} - dependsOn: - ${{ parameters.parentJobs }} + pool: vmImage: ${{ parameters.pool }} displayName: ${{ parameters.name }} Test - ${{ parameters.purpose }} - ${{ parameters.tagSet }} steps: - - pwsh: | - Get-ChildItem -Path env: - displayName: Capture Environment - condition: succeededOrFailed() - - - task: DownloadBuildArtifacts@0 - displayName: 'Download build artifacts' - inputs: - downloadType: specific - itemPattern: | - build/**/* - downloadPath: '$(System.ArtifactsDirectory)' - - - pwsh: | - Get-ChildItem "$(System.ArtifactsDirectory)\*" -Recurse - displayName: 'Capture Artifacts Directory' - continueOnError: true - - - pwsh: | - if (Test-Path -Path $HOME/.dotnet) { - Remove-Item $HOME/.dotnet -Recurse -Force - } - displayName: Remove Old .NET SDKs - condition: succeededOrFailed() - - - pwsh: | - Import-Module .\tools\ci.psm1 - Invoke-CIInstall -SkipUser - displayName: Bootstrap - - - task: ExtractFiles@1 - displayName: 'Extract Build ZIP' - inputs: - archiveFilePatterns: '$(System.ArtifactsDirectory)/build/build.zip' - destinationFolder: '$(System.ArtifactsDirectory)/bins' - - - bash: | - find "$(System.ArtifactsDirectory)/bins" -type d -exec chmod +rwx {} \; - find "$(System.ArtifactsDirectory)/bins" -type f -exec chmod +rw {} \; - displayName: 'Fix permissions' - continueOnError: true - - - pwsh: | - Get-ChildItem "$(System.ArtifactsDirectory)\bins\*" -Recurse -ErrorAction SilentlyContinue - displayName: 'Capture Extracted Build ZIP' - continueOnError: true - - - pwsh: | - Import-Module .\tools\ci.psm1 - Restore-PSOptions -PSOptionsPath '$(System.ArtifactsDirectory)\build\psoptions.json' - $options = (Get-PSOptions) - $rootPath = '$(System.ArtifactsDirectory)\bins' - $originalRootPath = Split-Path -path $options.Output - $path = Join-Path -path $rootPath -ChildPath (split-path -leaf -path $originalRootPath) - $pwshPath = Join-Path -path $path -ChildPath 'pwsh' - chmod a+x $pwshPath - $options.Output = $pwshPath - Set-PSOptions $options - Invoke-CITest -Purpose '${{ parameters.purpose }}' -TagSet '${{ parameters.tagSet }}' - displayName: Test - condition: succeeded() + - template: ./test/nix-test-steps.yml + parameters: + purpose: ${{ parameters.purpose }} + tagSet: ${{ parameters.tagSet }} diff --git a/.vsts-ci/templates/test/nix-container-test.yml b/.vsts-ci/templates/test/nix-container-test.yml new file mode 100644 index 00000000000..931af6fc675 --- /dev/null +++ b/.vsts-ci/templates/test/nix-container-test.yml @@ -0,0 +1,30 @@ +parameters: + pool: 'macOS-latest' + purpose: '' + tagSet: 'CI' + name: 'mac' + +jobs: +- job: ${{ parameters.name }}_test_${{ parameters.purpose }}_${{ parameters.tagSet }} + + dependsOn: + - getContainerJob + + variables: + __INCONTAINER: 1 + getContainerJob: $[ dependencies.getContainerJob.outputs['getContainerTask.containerName'] ] + containerBuildName: $[ dependencies.getContainerJob.outputs['getContainerTask.containerBuildName'] ] + + container: $[ variables.getContainerJob ] + + pool: + vmImage: ${{ parameters.pool }} + + displayName: ${{ parameters.name }} Test - ${{ parameters.purpose }} - ${{ parameters.tagSet }} + + steps: + - template: ./nix-test-steps.yml + parameters: + purpose: ${{ parameters.purpose }} + tagSet: ${{ parameters.tagSet }} + buildName: $(containerBuildName) diff --git a/.vsts-ci/templates/test/nix-test-steps.yml b/.vsts-ci/templates/test/nix-test-steps.yml new file mode 100644 index 00000000000..84d7a37b848 --- /dev/null +++ b/.vsts-ci/templates/test/nix-test-steps.yml @@ -0,0 +1,60 @@ +parameters: + purpose: '' + tagSet: 'CI' + buildName: 'Ubuntu' + +steps: + - pwsh: | + Get-ChildItem -Path env: + displayName: Capture Environment + condition: succeededOrFailed() + + - task: DownloadBuildArtifacts@0 + displayName: 'Download build artifacts' + inputs: + downloadType: specific + itemPattern: | + build/**/* + downloadPath: '$(System.ArtifactsDirectory)' + + - pwsh: | + Get-ChildItem "$(System.ArtifactsDirectory)\*" -Recurse + displayName: 'Capture Artifacts Directory' + continueOnError: true + + - pwsh: | + Import-Module .\tools\ci.psm1 + Invoke-CIInstall -SkipUser + displayName: Bootstrap + + - task: ExtractFiles@1 + displayName: 'Extract Build ZIP' + inputs: + archiveFilePatterns: '$(System.ArtifactsDirectory)/build/build.zip' + destinationFolder: '$(System.ArtifactsDirectory)/bins' + + - bash: | + find "$(System.ArtifactsDirectory)/bins" -type d -exec chmod +rwx {} \; + find "$(System.ArtifactsDirectory)/bins" -type f -exec chmod +rw {} \; + displayName: 'Fix permissions' + continueOnError: true + + - pwsh: | + Get-ChildItem "$(System.ArtifactsDirectory)\bins\*" -Recurse -ErrorAction SilentlyContinue + displayName: 'Capture Extracted Build ZIP' + continueOnError: true + + - pwsh: | + Import-Module .\tools\ci.psm1 + Restore-PSOptions -PSOptionsPath '$(System.ArtifactsDirectory)\build\psoptions.json' + $options = (Get-PSOptions) + $rootPath = '$(System.ArtifactsDirectory)\bins' + $originalRootPath = Split-Path -path $options.Output + $path = Join-Path -path $rootPath -ChildPath (split-path -leaf -path $originalRootPath) + $pwshPath = Join-Path -path $path -ChildPath 'pwsh' + chmod a+x $pwshPath + $options.Output = $pwshPath + Set-PSOptions $options + Invoke-CITest -Purpose '${{ parameters.purpose }}' -TagSet '${{ parameters.tagSet }}' -TitlePrefix '${{ parameters.buildName }}' + displayName: Test + condition: succeeded() diff --git a/.vsts-ci/templates/verify-xunit.yml b/.vsts-ci/templates/verify-xunit.yml index 9e09584d5c3..b43cb9339f9 100644 --- a/.vsts-ci/templates/verify-xunit.yml +++ b/.vsts-ci/templates/verify-xunit.yml @@ -1,6 +1,6 @@ parameters: parentJobs: [] - pool: 'vs2017-win2016' + pool: 'windows-latest' jobName: 'xunit_verify' jobs: @@ -19,12 +19,12 @@ jobs: xunit/**/* downloadPath: '$(System.ArtifactsDirectory)' - - powershell: | + - pwsh: | dir "$(System.ArtifactsDirectory)\*" -Recurse displayName: 'Capture artifacts directory' continueOnError: true - - powershell: | + - pwsh: | Import-Module .\tools\ci.psm1 $xUnitTestResultsFile = "$(System.ArtifactsDirectory)\xunit\xUnitTestResults.xml" diff --git a/.vsts-ci/templates/windows-test.yml b/.vsts-ci/templates/windows-test.yml index b316f8f360e..2e5d5b67e24 100644 --- a/.vsts-ci/templates/windows-test.yml +++ b/.vsts-ci/templates/windows-test.yml @@ -1,5 +1,5 @@ parameters: - pool: 'Hosted VS2017' + pool: 'windows-2019' parentJobs: [] purpose: '' tagSet: 'CI' @@ -9,7 +9,7 @@ jobs: dependsOn: ${{ parameters.parentJobs }} pool: - name: ${{ parameters.pool }} + vmImage: ${{ parameters.pool }} displayName: Windows Test - ${{ parameters.purpose }} - ${{ parameters.tagSet }} diff --git a/.vsts-ci/windows-daily.yml b/.vsts-ci/windows-daily.yml index 9171112b362..15d1a82fc3f 100644 --- a/.vsts-ci/windows-daily.yml +++ b/.vsts-ci/windows-daily.yml @@ -53,7 +53,7 @@ stages: jobs: - job: win_test pool: - vmImage: vs2017-win2016 + vmImage: windows-2019 displayName: Windows Test timeoutInMinutes: 90 diff --git a/.vsts-ci/windows.yml b/.vsts-ci/windows.yml index e96b320d77b..f886d2bd337 100644 --- a/.vsts-ci/windows.yml +++ b/.vsts-ci/windows.yml @@ -11,9 +11,9 @@ trigger: include: - '*' exclude: - - /.vsts-ci/misc-analysis.yml - - /.github/ISSUE_TEMPLATE/* - - /.dependabot/config.yml + - .vsts-ci/misc-analysis.yml + - .github/ISSUE_TEMPLATE/* + - .dependabot/config.yml - test/perf/* pr: branches: @@ -28,11 +28,15 @@ pr: - .dependabot/config.yml - .github/ISSUE_TEMPLATE/* - .vsts-ci/misc-analysis.yml + - tools/cgmanifest.json + - LICENSE.txt - test/common/markdown/* - test/perf/* - tools/packaging/* - tools/releaseBuild/* - tools/releaseBuild/azureDevOps/templates/* + - README.md + - .spelling variables: GIT_CONFIG_PARAMETERS: "'core.autocrlf=false'" diff --git a/.vsts-ci/windows/templates/windows-packaging.yml b/.vsts-ci/windows/templates/windows-packaging.yml index 19d2be32618..90655ee6298 100644 --- a/.vsts-ci/windows/templates/windows-packaging.yml +++ b/.vsts-ci/windows/templates/windows-packaging.yml @@ -1,25 +1,53 @@ parameters: - pool: 'Hosted VS2017' - jobName: 'win_packaging' - architecture: 'x64' - channel: 'preview' - parentJobs: [] + - name: pool + default: 'windows-latest' + - name: jobName + default: 'win_packaging' + - name: runtimePrefix + default: 'win7' + - name: architecture + default: 'x64' + - name: channel + default: 'preview' jobs: - job: ${{ parameters.jobName }}_${{ parameters.channel }}_${{ parameters.architecture }} - dependsOn: - ${{ parameters.parentJobs }} + + variables: + - name: repoFolder + value: PowerShell + - name: repoPath + value: $(Agent.BuildDirectory)\$(repoFolder) + - name: complianceRepoFolder + value: compliance + - name: complianceRepoPath + value: $(Agent.BuildDirectory)\$(complianceRepoFolder) + pool: - name: ${{ parameters.pool }} + vmImage: ${{ parameters.pool }} displayName: Windows Packaging - ${{ parameters.architecture }} - ${{ parameters.channel }} steps: + - checkout: self + clean: true + path: $(repoFolder) + + - checkout: ComplianceRepo + clean: true + path: $(complianceRepoFolder) + - powershell: | Get-ChildItem -Path env: displayName: Capture environment condition: succeededOrFailed() + - pwsh: | + $PSVersionTable + displayName: Capture PowerShell Version Table + condition: succeededOrFailed() + + - template: /tools/releaseBuild/azureDevOps/templates/insert-nuget-config-azfeed.yml - pwsh: | @@ -27,9 +55,33 @@ jobs: Invoke-CIInstall -SkipUser displayName: Bootstrap condition: succeeded() + workingDirectory: $(repoPath) + + - pwsh: | + Import-Module .\tools\ci.psm1 + New-CodeCoverageAndTestPackage + Invoke-CIFinish -Runtime ${{ parameters.runtimePrefix }}-${{ parameters.architecture }} -channel ${{ parameters.channel }} -Stage Build + displayName: Build + workingDirectory: $(repoPath) + + - template: Sbom.yml@ComplianceRepo + parameters: + BuildDropPath: '$(System.ArtifactsDirectory)/mainBuild' + Build_Repository_Uri: $(build.repository.uri) + displayName: SBOM + sourceScanPath: '$(repoPath)\tools' + + # This is needed as SBOM task removed the installed .NET and installs .NET 3.1 + - pwsh: | + Import-Module .\tools\ci.psm1 + Invoke-CIInstall -SkipUser + displayName: Bootstrap + condition: succeeded() + workingDirectory: $(repoPath) - pwsh: | Import-Module .\tools\ci.psm1 New-CodeCoverageAndTestPackage - Invoke-CIFinish -Runtime win7-${{ parameters.architecture }} -channel ${{ parameters.channel }} - displayName: Build and Test Package + Invoke-CIFinish -Runtime ${{ parameters.runtimePrefix }}-${{ parameters.architecture }} -channel ${{ parameters.channel }} -Stage Package + displayName: Package and Test + workingDirectory: $(repoPath) diff --git a/.vsts-ci/windows/windows-packaging.yml b/.vsts-ci/windows/windows-packaging.yml index f471196d963..b413b7a639b 100644 --- a/.vsts-ci/windows/windows-packaging.yml +++ b/.vsts-ci/windows/windows-packaging.yml @@ -51,6 +51,7 @@ pr: - src/Microsoft.WSMan.Runtime/Microsoft.WSMan.Runtime.csproj - src/Modules/PSGalleryModules.csproj - src/powershell-win-core/powershell-win-core.csproj + - test/packaging/windows/* - tools/ci.psm1 - tools/packaging/* @@ -67,10 +68,17 @@ variables: - name: __SuppressAnsiEscapeSequences value: 1 - group: fakeNugetKey + - name: SBOMGenerator_Formats + value: spdx:2.2 resources: -- repo: self - clean: true + repositories: + - repository: ComplianceRepo + type: github + endpoint: PowerShell + name: PowerShell/compliance + ref: master + stages: - stage: PackagingWin displayName: Packaging for Windows @@ -86,3 +94,13 @@ stages: parameters: channel: preview architecture: x86 + - template: templates/windows-packaging.yml + parameters: + channel: preview + architecture: arm + runtimePrefix: win + - template: templates/windows-packaging.yml + parameters: + channel: preview + architecture: arm64 + runtimePrefix: win diff --git a/ADOPTERS.md b/ADOPTERS.md index 110f56d16e6..faef4252621 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -34,4 +34,6 @@ This is a list of adopters of using PowerShell in production or in their product * [GitHub Actions Virtual Environments for Hosted Runners](https://help.github.com/actions/reference/virtual-environments-for-github-hosted-runners) Windows, Ubuntu, and macOS virtual environments used by customers of GitHub Actions include PowerShell out of the box. * [GitHub Actions Python builds](https://github.com/actions/python-versions) GitHub Actions uses PowerShell to automate building Python from source for its runners. * [Microsoft HoloLens](https://www.microsoft.com/hololens) makes extensive use of PowerShell 7+ throughout the development cycle to automate tasks such as firmware assembly and automated testing. +* [Power BI](https://powerbi.microsoft.com/) provides PowerShell users a set of cmdlets in [MicrosoftPowerBIMgmt](https://docs.microsoft.com/powershell/power-bi) module to manage and automate the Power BI service. + This is in addition to Power BI leveraging PowerShell internally for various engineering systems and infrastructure for its service. * [Windows 10 IoT Core](https://docs.microsoft.com/windows/iot-core/windows-iot-core) is a small form factor Windows edition for IoT devices and now you can easily include the [PowerShell package](https://github.com/ms-iot/iot-adk-addonkit/blob/master/Tools/IoTCoreImaging/Docs/Import-PSCoreRelease.md#Import-PSCoreRelease) in your imaging process. diff --git a/Analyzers.props b/Analyzers.props index b67dba7625b..e21c552467a 100644 --- a/Analyzers.props +++ b/Analyzers.props @@ -1,7 +1,7 @@ - - + + diff --git a/CHANGELOG/6.0.md b/CHANGELOG/6.0.md index 948197f0619..52db53afabf 100644 --- a/CHANGELOG/6.0.md +++ b/CHANGELOG/6.0.md @@ -766,9 +766,8 @@ For more information on this, we invite you to read [this blog post explaining P ### Move to .NET Core 2.0 (.NET Standard 2.0 support) PowerShell Core has moved to using .NET Core 2.0 so that we can leverage all the benefits of .NET Standard 2.0. (#3556) -To learn more about .NET Standard 2.0, there's some great starter content [on Youtube](https://www.youtube.com/playlist?list=PLRAdsfhKI4OWx321A_pr-7HhRNk7wOLLY), -on [the .NET blog](https://devblogs.microsoft.com/dotnet/introducing-net-standard/), -and [on GitHub](https://github.com/dotnet/standard/blob/master/docs/faq.md). +To learn more about .NET Standard 2.0, there's some great starter content [on Youtube](https://www.youtube.com/playlist?list=PLRAdsfhKI4OWx321A_pr-7HhRNk7wOLLY) +and on [the .NET blog](https://devblogs.microsoft.com/dotnet/introducing-net-standard/). We'll also have more content soon in our [repository documentation](https://github.com/PowerShell/PowerShell/tree/master/docs) (which will eventually make its way to [official documentation](https://github.com/powershell/powershell-docs)). In a nutshell, .NET Standard 2.0 allows us to have universal, portable modules between Windows PowerShell (which uses the full .NET Framework) and PowerShell Core (which uses .NET Core). Many modules and cmdlets that didn't work in the past may now work on .NET Core, so import your favorite modules and tell us what does and doesn't work in our GitHub Issues! diff --git a/CHANGELOG/7.0.md b/CHANGELOG/7.0.md index 9b7c0a9f45b..13468c71bc8 100644 --- a/CHANGELOG/7.0.md +++ b/CHANGELOG/7.0.md @@ -1,5 +1,160 @@ # 7.0 Changelog +## [7.0.11] - 2022-05-17 + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 3.1.419

+ +
+ +
    +
  • Add explicit job name for approval tasks in Snap stage (#16579)
  • +
  • Update to use mcr.microsoft.com (#17272)
  • +
  • Update global.json and wix
  • +
  • Put Secure supply chain analysis at correct place (#17273)
  • +
  • Partial back-port of: Update a few tests to make them more stable in CI (#16944) (Internal 20648)
  • +
  • Replace . in notices container name (#17292)
  • +
  • Add an approval for releasing build-info json (#16351)
  • +
  • Release build info json when it is preview (#16335)
  • +
  • Add a major-minor build info JSON file (#16301)
  • +
  • Update release instructions with link to new build (#17256)
  • +
  • Add condition to generate release file in local dev build only (#17255)
  • +
  • Removed old not-used-anymore docker-based tests for PS release packages (#16224)
  • +
  • Publish global tool package for stable releases (#15961)
  • +
  • Update to use windows-latest as the build agent image (#16831)
  • +
  • Don't upload dep or tar.gz for RPM build because there are none. (#17224)
  • +
  • Update to vPack task version 12 (#17225)
  • +
  • Make RPM license recognized (#17223)
  • +
  • Ensure psoptions.json and manifest.spdx.json files always exist in packages (#17226)
  • +
+ +
+ +[7.0.11]: https://github.com/PowerShell/PowerShell/compare/v7.0.10...v7.0.11 + +## [7.0.10] - 2022-04-26 + +### Engine Updates and Fixes + +- Fix for partial PowerShell module search paths, that can be resolved to CWD locations +- Do not include node names when sending telemetry. (#16981) to v7.0.10 (Internal 20186,Internal 20261) + +### Tests + +- Re-enable `PowerShellGet` tests targeting PowerShell gallery (#17062) +- Skip failing scriptblock tests (#17093) + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 3.1.418

+ +
+ +
    +
  • Fixed package names verification to support multi-digit versions (Internal 20363)
  • +
  • Fix build failure in `generate checksum file for packages` step - v7.0.10 (Internal 20275)
  • +
  • Updated files.wxs for 7.0.10 (Internal 20208)
  • +
  • Updated to .NET 3.1.24 / SDK 3.1.418 (Internal 20133)
  • +
  • Disable broken macOS CI job, which is unused (Internal 20189)
  • +
  • Update Ubuntu images to use Ubuntu 20.04 (#15906)
  • +
  • Update dotnet-install script download link (Internal 19949)
  • +
  • Create checksum file for global tools (Internal 19934)
  • +
  • Make sure global tool packages are published in stable build (Internal 19623)
  • +
+ +
+ +[7.0.10]: https://github.com/PowerShell/PowerShell/compare/v7.0.9...v7.0.10 + +## [7.0.9] - 2022-03-16 + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 3.1.417

+ +
+ +
    +
  • Fix the NuGet SDK package creation (Internal 19569)
  • +
  • Fix NuGet package compliance issues (#13045)
  • +
  • Fix issues in release build (#16332)
  • +
  • Enable ARM64 packaging for macOS (#15768)
  • +
  • Update feed and analyzer dependency (#16327)
  • +
  • Only upload stable buildinfo for stable releases (#16251)
  • +
  • Opt-in to build security monitoring (#16911)
  • +
  • Update experimental feature json files (#16838) (Thanks @!)
  • +
  • Ensure alpine and arm SKUs have the PowerShell configuration file with experimental features enabled (#16823)
  • +
  • Remove WiX install (#16834)
  • +
  • Add Linux package dependencies for packaging (#16807)
  • +
  • Switch to our custom images for build and release (#16801)
  • +
  • Remove all references to cmake for the builds in this repo (#16578)
  • +
  • Register NuGet source when generating CGManifest (#16570)
  • +
  • Update Images used for release (#16580)
  • +
  • Add Software Bill of Materials to the main packages (#16202, #16641, #16711)
  • +
  • Add GitHub Workflow to keep notices up to date (#16284)
  • +
  • Update the vmImage and PowerShell root directory for macOS builds (#16611)
  • +
  • Update macOS build image and root folder for build (#16609)
  • +
  • Add checkout to build json stage to get ci.psm1 (#16399)
  • +
  • Move mapping file into product repo and add Debian 11 (#16316)
  • +
+ +
+ +[7.0.9]: https://github.com/PowerShell/PowerShell/compare/v7.0.8...v7.0.9 + +## [7.0.8] - 2021-10-14 + +### Engine Updates and Fixes + +- Handle error from unauthorized access when removing `AppLocker` test files (#15881) +- Handle error when the telemetry mutex cannot be created (#15574) (Thanks @gukoff!) +- Configure `ApplicationInsights` to not send cloud role name (Internal 17099) +- Disallow `Add-Type` in NoLanguage mode on a locked down machine (Internal 17521) + +### Tools + +- Add `.stylecop` to `filetypexml` and format it (#16025) + +### Build and Packaging Improvements + +
+ + +

Bump .NET SDK to 3.1.414

+
+ +
    +
  • Update the nuget.config file used for building NuGet packages (Internal 17547)
  • +
  • Sign the .NET createdump executable (#16229)
  • +
  • Upgrade set-value package for markdown test (#16196)
  • +
  • Move vPack build to 1ES Pool (#16169)
  • +
  • Update to .NET SDK 3.1.414 (Internal 17532)
  • +
  • Fix the macOS build by updating the pool image name (#16010)
  • +
  • Move from PkgES hosted agents to 1ES hosted agents (#16023)
  • +
  • Use Alpine 3.12 for building PowerShell for Alpine Linux (#16008)
  • +
+ +
+ +### Documentation and Help Content + +- Fix example nuget.config (#14349) + +[7.0.8]: https://github.com/PowerShell/PowerShell/compare/v7.0.7...v7.0.8 + ## [7.0.7] - 2021-08-12 ### Build and Packaging Improvements @@ -149,6 +304,8 @@ Bump .NET SDK to version 3.1.405 +[7.0.3]: https://github.com/PowerShell/PowerShell/compare/v7.0.2...v7.0.3 + ## [7.0.2] - 2020-06-11 ### Engine Updates and Fixes @@ -1187,7 +1344,6 @@ Move to .NET Core 3.1.202 SDK and update packages. - Update docs for `6.2.0-rc.1` release (#9022) - Update release template (#8996) - [7.0.3]: https://github.com/PowerShell/PowerShell/compare/v7.0.2...v7.0.3 [7.0.2]: https://github.com/PowerShell/PowerShell/compare/v7.0.1...v7.0.2 [7.0.1]: https://github.com/PowerShell/PowerShell/compare/v7.0.0...v7.0.1 diff --git a/CHANGELOG/7.1.md b/CHANGELOG/7.1.md index 91d00b70a49..eba1998e29c 100644 --- a/CHANGELOG/7.1.md +++ b/CHANGELOG/7.1.md @@ -1,5 +1,121 @@ # 7.1 Changelog +## [7.1.7] - 2022-04-26 + +### Engine Updates and Fixes + +- Fix for partial PowerShell module search paths, that can be resolved to CWD locations +- Do not include node names when sending telemetry. (#16981) to v7.1.7 (Internal 20187,Internal 20260) + +### Tests + +- Re-enable `PowerShellGet` tests targeting PowerShell gallery (#17062) +- Skip failing scriptblock tests (#17093) + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 5.0.407

+ +
+ +
    +
  • Fix build failure in `generate checksum file for packages` step - v7.1.7 (Internal 20274)
  • +
  • Updated files.wxs for 7.1.7 (Internal 20210)
  • +
  • Updated to .NET 5.0.16 / SDK 5.0.407 (Internal 20131)
  • +
  • Update Ubuntu images to use Ubuntu 20.04 (#15906)
  • +
  • Update dotnet-install script download link (Internal 19950)
  • +
  • Create checksum file for global tools (#17056) (Internal 19928)
  • +
  • Make sure global tool packages are published in stable build (Internal 19624)
  • +
+ +
+ +[7.1.7]: https://github.com/PowerShell/PowerShell/compare/v7.1.6...v7.1.7 + +## [7.1.6] - 2022-03-16 + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 5.0.406

+ +
+ +
    +
  • Update the mapping file (#16316, Internal 19528)
  • +
  • Remove code that handles dotnet5 feed (Internal 19525)
  • +
  • Fix issues in release build (#16332)
  • +
  • Enable ARM64 packaging for macOS (#15768)
  • +
  • Update feed and analyzer dependency (#16327)
  • +
  • Only upload stable buildinfo for stable releases (#16251)
  • +
  • Opt-in to build security monitoring (#16911)
  • +
  • Update experimental feature json files (#16838)
  • +
  • Ensure alpine and arm SKUs have the PowerShell configuration file with experimental features enabled (#16823)
  • +
  • Remove WiX install (#16834)
  • +
  • Add Linux package dependencies for packaging (#16807)
  • +
  • Switch to our custom images for build and release (#16801)
  • +
  • Remove all references to cmake for the builds in this repo (#16578)
  • +
  • Register NuGet source when generating CGManifest (#16570)
  • +
  • Update images used for release (#16580)
  • +
  • Add GitHub Workflow to keep notices up to date (#16284)
  • +
  • Update the vmImage and PowerShell root directory for macOS builds (#16611)
  • +
  • Add Software Bill of Materials to the main packages (#16202, #16641, #16711)
  • +
  • Update macOS build image and root folder for build (#16609)
  • +
  • Add diagnostics used to take corrective action when releasing buildInfo JSON file (#16404)
  • +
  • Add checkout to build json stage to get ci.psm1 (#16399)
  • +
+ +
+ +[7.1.6]: https://github.com/PowerShell/PowerShell/compare/v7.1.5...v7.1.6 + +## [7.1.5] - 2021-10-14 + +### Engine Updates and Fixes + +- Handle error from unauthorized access when removing `AppLocker` test files (#15881) +- Test more thoroughly whether a command is `Out-Default` for transcription scenarios (#15653) +- Handle error when the telemetry mutex cannot be created (#15574) (Thanks @gukoff!) +- Configure `ApplicationInsights` to not send cloud role name (Internal 17100) +- Disallow `Add-Type` in NoLanguage mode on a locked down machine (Internal 17522) + +### Tools + +- Add `.stylecop` to `filetypexml` and format it (#16025) + +### Build and Packaging Improvements + +
+ + +

Bump .NET SDK to 5.0.402

+
+ +
    +
  • Upgrade set-value package for markdown test (#16196)
  • +
  • Sign the .NET createdump executable (#16229)
  • +
  • Move vPack build to 1ES Pool (#16169)
  • +
  • Update to .NET SDK 5.0.402 (Internal 17537)
  • +
  • Move from PkgES hosted agents to 1ES hosted agents (#16023)
  • +
  • Fix the macOS build by updating the pool image name (#16010)
  • +
  • Use Alpine 3.12 for building PowerShell for Alpine Linux (#16008)
  • +
+ +
+ +### Documentation and Help Content + +- Fix example nuget.config (#14349) + +[7.1.5]: https://github.com/PowerShell/PowerShell/compare/v7.1.4...v7.1.5 + ## [7.1.4] - 2021-08-12 ### Build and Packaging Improvements diff --git a/CHANGELOG/7.2.md b/CHANGELOG/7.2.md new file mode 100644 index 00000000000..77890905734 --- /dev/null +++ b/CHANGELOG/7.2.md @@ -0,0 +1,1306 @@ +# 7.2 Changelog + +## [7.2.5] - 2022-06-21 + +### Engine Updates and Fixes + +- Fix native library loading for osx-arm64 (#17495) (Thanks @awakecoding!) + +### Tests + +- Make Assembly Load Native test work on a FX Dependent Linux Install (#17496) +- Enable more tests to be run in a container. (#17294) +- Switch to using GitHub action to verify markdown links for PRs (#17281) +- Try to stabilize a few tests that fail intermittently (#17426) +- TLS test fix back-port (#17424) + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK to 6.0.301 (Internal 21218)

+ +
+ +
    +
  • Update Wix file (Internal 21242)
  • +
  • Conditionally add output argument
  • +
  • Rename mariner package to cm (#17506)
  • +
  • Backport test fixes for 7.2 (#17494)
  • +
  • Update dotnet-runtime version (#17472)
  • +
  • Update to use windows-latest as the build agent image (#17418)
  • +
  • Publish preview versions of mariner to preview repo (#17464)
  • +
  • Move cgmanifest generation to daily (#17258)
  • +
  • Fix mariner mappings (#17413)
  • +
  • Make sure we execute tests on LTS package for older LTS releases (#17430)
  • +
  • Add a finalize template which causes jobs with issues to fail (#17428)
  • +
  • Make mariner packages Framework dependent (#17425)
  • +
  • Base work for adding mariner amd64 package (#17417)
  • +
+ +
+ +[7.2.5]: https://github.com/PowerShell/PowerShell/compare/v7.2.4...v7.2.5 + +## [7.2.4] - 2022-05-17 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK to 6.0.203

+ +
+ +
    +
  • Add mapping for Ubuntu22.04 Jammy (#17317)
  • +
  • Update to use mcr.microsoft.com (#17272)
  • +
  • Update third party notices
  • +
  • Update global.json and wix
  • +
  • Put Secure supply chain analysis at correct place (#17273)
  • +
  • Fix web cmdlets so that an empty Get does not include a content-length header (#16587)
  • +
  • Update package fallback list for Ubuntu (from those updated for Ubuntu 22.04) (deb) (#17217)
  • +
  • Add sha256 digests to RPM packages (#17215)
  • +
  • Allow multiple installations of dotnet. (#17216)
  • +
+ +
+ +[7.2.4]: https://github.com/PowerShell/PowerShell/compare/v7.2.3...v7.2.4 + +## [7.2.3] - 2022-04-26 + +### Engine Updates and Fixes + +- Fix for partial PowerShell module search paths, that can be resolved to CWD locations (Internal 20126) +- Do not include node names when sending telemetry. (#16981) to v7.2.3 (Internal 20188) + +### Tests + +- Re-enable `PowerShellGet` tests targeting PowerShell gallery (#17062) +- Skip failing scriptblock tests (#17093) + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK to 6.0.202

+ +
+ +
    +
  • Making NameObscurerTelemetryInitializer internal - v7.2.3 (Internal 20239)
  • +
  • Updated files.wxs for 7.2.3 (Internal 20211)
  • +
  • Updated ThirdPartyNotices for 7.2.3 (Internal 20199)
  • +
  • Work around issue with notice generation
  • +
  • Replace . in notices container name
  • +
  • Updated cgmanifest.json by findMissingNotices.ps1 in v7.2.3 (Internal 20190)
  • +
  • v7.2.3 - Updated packages using dotnet-outdated global tool (Internal 20170)
  • +
  • Updated to .NET 6.0.4 / SDK 6.0.202 (Internal 20128)
  • +
  • Update dotnet-install script download link (Internal 19951)
  • +
  • Create checksum file for global tools (#17056) (Internal 19935)
  • +
  • Make sure global tool packages are published in stable build (Internal 19625)
  • +
  • Fix release pipeline (Internal 19617)
  • +
+ +
+ +[7.2.3]: https://github.com/PowerShell/PowerShell/compare/v7.2.2...v7.2.3 + +## [7.2.2] - 2022-03-16 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK to 6.0.201

+ +
+ +
    +
  • Update WiX file (Internal 19460)
  • +
  • Update .NET SDK version to 6.0.201 (Internal 19457)
  • +
  • Update experimental feature JSON files (#16838)
  • +
  • Ensure Alpine and ARM SKUs have powershell.config.json file with experimental features enabled (#16823)
  • +
  • Update the vmImage and PowerShell root directory for macOS builds (#16611)
  • +
  • Update macOS build image and root folder for build (#16609)
  • +
  • Remove WiX install (#16834)
  • +
  • Opt-in to build security monitoring (#16911)
  • +
  • Add SBOM manifest for release packages (#16641, #16711)
  • +
  • Add Linux package dependencies for packaging (#16807)
  • +
  • Switch to our custom images for build and release (#16801, #16580)
  • +
  • Remove all references to cmake for the builds in this repo (#16578)
  • +
  • Register NuGet source when generating CGManifest (#16570)
  • +
+ +
+ +[7.2.2]: https://github.com/PowerShell/PowerShell/compare/v7.2.1...v7.2.2 + +## [7.2.1] - 2021-12-14 + +### General Cmdlet Updates and Fixes + +- Remove declaration of experimental features in Utility module manifest as they are stable (#16460) +- Bring back pwsh.exe for framework dependent packages to support Start-Job (#16535) +- Change default for `$PSStyle.OutputRendering` to `Ansi` (Internal 18394) +- Update `HelpInfoUri` for 7.2 release (#16456) +- Fix typo for "privacy" in MSI installer (#16407) + +### Tests + +- Set clean state before testing `UseMU` in the MSI (#16543) + +### Build and Packaging Improvements + +
+ +
    +
  • Add explicit job name for approval tasks in Snap stage (#16579)
  • +
  • Fixing the build by removing duplicate TSAUpload entries (Internal 18399)
  • +
  • Port CGManifest fixes (Internal 18402)
  • +
  • Update CGManifest (Internal 18403)
  • +
  • Updated package dependencies for 7.2.1 (Internal 18388)
  • +
  • Use different containers for different branches (#16434)
  • +
  • Use notice task to generate license assuming CGManifest contains all components (#16340)
  • +
  • Create compliance build (#16286)
  • +
  • Update release instructions with link to new build (#16419)
  • +
  • Add diagnostics used to take corrective action when releasing buildInfoJson (#16404)
  • +
  • vPack release should use buildInfoJson new to 7.2 (#16402)
  • +
  • Add checkout to build json stage to get ci.psm1 (#16399)
  • +
  • Update the usage of metadata.json for getting LTS information (#16381)
  • +
  • Move mapping file into product repo and add Debian 11 (#16316)
  • +
+ +
+ +[7.2.1]: https://github.com/PowerShell/PowerShell/compare/v7.2.0...v7.2.1 + +## [7.2.0] - 2021-11-08 + +### General Cmdlet Updates and Fixes + +- Handle exception when trying to resolve a possible link path (#16310) + +### Tests + +- Fix global tool and SDK tests in release pipeline (#16342) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@kondratyev-nv

+ +
+ +
    +
  • Add an approval for releasing build-info json (#16351)
  • +
  • Release build info json when it is preview (#16335)
  • +
  • Update metadata.json for v7.2.0 release
  • +
  • Update to the latest notices file and update cgmanifest.json (#16339)(#16325)
  • +
  • Fix issues in release build by updating usage of powershell.exe with pwsh.exe (#16332)
  • +
  • Update feed and analyzer dependency (#16327)
  • +
  • Update to .NET 6 GA build 6.0.100-rtm.21527.11 (#16309)
  • +
  • Add a major-minor build info JSON file (#16301)
  • +
  • Fix Windows build ZIP packaging (#16299) (Thanks @kondratyev-nv!)
  • +
  • Clean up crossgen related build scripts also generate native symbols for R2R images (#16297)
  • +
  • Fix issues reported by code signing verification tool (#16291)
  • +
+ +
+ +[7.2.0]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-rc.1...v7.2.0 + +## [7.2.0-rc.1] - 2021-10-21 + +### General Cmdlet Updates and Fixes + +- Disallow COM calls for AppLocker system lockdown (#16268) +- Configure `Microsoft.ApplicationInsights` to not send cloud role name (#16246) +- Disallow `Add-Type` in NoLanguage mode on a locked down machine (#16245) +- Make property names for color VT100 sequences consistent with documentation (#16212) +- Make moving a directory into itself with `Move-Item` an error (#16198) +- Change `FileSystemInfo.Target` from a `CodeProperty` to an `AliasProperty` that points to `FileSystemInfo.LinkTarget` (#16165) + +### Tests + +- Removed deprecated docker-based tests for PowerShell release packages (#16224) + +### Build and Packaging Improvements + +
+ + +

Bump .NET SDK to 6.0.100-rc.2

+
+ +
    +
  • Update .NET 6 to version 6.0.100-rc.2.21505.57 (#16249)
  • +
  • Fix RPM packaging (Internal 17704)
  • +
  • Update ThirdPartyNotices.txt (#16283)
  • +
  • Update pipeline yaml file to use ubuntu-latest image (#16279)
  • +
  • Add script to generate cgmanifest.json (#16278)
  • +
  • Update version of Microsoft.PowerShell.Native and Microsoft.PowerShell.MarkdownRender packages (#16277)
  • +
  • Add cgmanifest.json for generating correct third party notice file (#16266)
  • +
  • Only upload stable buildinfo for stable releases (#16251)
  • +
  • Don't upload .dep or .tar.gz for RPM because there are none (#16230)
  • +
  • Ensure RPM license is recognized (#16189)
  • +
  • Add condition to only generate release files in local dev build only (#16259)
  • +
  • Ensure psoptions.json and manifest.spdx.json files always exist in packages (#16258)
  • +
  • Fix CI script and split out ARM runs (#16252)
  • +
  • Update vPack task version to 12 (#16250)
  • +
  • Sign third party executables (#16229)
  • +
  • Add Software Bill of Materials to the main packages (#16202)
  • +
  • Upgrade set-value package for markdown test (#16196)
  • +
  • Fix Microsoft update spelling issue (#16178)
  • +
  • Move vPack build to 1ES Pool (#16169)
  • +
+ +
+ +[7.2.0-rc.1]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.10...v7.2.0-rc.1 + +## [7.2.0-preview.10] - 2021-09-28 + +### Engine Updates and Fixes + +- Remove duplicate remote server mediator code (#16027) + +### General Cmdlet Updates and Fixes + +- Use `PlainText` when writing to a host that doesn't support VT (#16092) +- Remove support for `AppExecLinks` to retrieve target (#16044) +- Move `GetOuputString()` and `GetFormatStyleString()` to `PSHostUserInterface` as public API (#16075) +- Add `isOutputRedirected` parameter to `GetFormatStyleString()` method (#14397) +- Fix `ConvertTo-SecureString` with key regression due to .NET breaking change (#16068) +- Fix regression in `Move-Item` to only fallback to `CopyAndDelete` in specific cases (#16029) +- Set `$?` correctly for command expression with redirection (#16046) +- Use `CurrentCulture` when handling conversions to `DateTime` in `Add-History` (#16005) (Thanks @vexx32!) +- Fix `NullReferenceException` in `Format-Wide` (#15990) (Thanks @DarylGraves!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze!

+ +
+ +
    +
  • Improve CommandInvocationIntrinsics API documentation and style (#14369)
  • +
  • Use bool?.GetValueOrDefault() in FormatWideCommand (#15988) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Fix typo in build.psm1 (#16038) (Thanks @eltociear!) +- Add `.stylecop` to `filetypexml` and format it (#16025) +- Enable sending Teams notification when workflow fails (#15982) + +### Tests + +- Enable two previously disabled `Get-Process` tests (#15845) (Thanks @iSazonov!) + +### Build and Packaging Improvements + +
+ + +Details + + +
    +
  • Add SHA256 hashes to release (#16147)
  • +
  • Update Microsoft.CodeAnalysis.CSharp version (#16138)
  • +
  • Change path for Component Governance for build to the path we actually use to build (#16137)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#16070) (#16045) (#16036) (#16021) (#15985)
  • +
  • Update .NET to 6.0.100-rc.1.21458.32 (#16066)
  • +
  • Update minimum required OS version for macOS (#16088)
  • +
  • Ensure locale is set correctly on Ubuntu 20.04 in CI (#16067) (#16073)
  • +
  • Update .NET SDK version from 6.0.100-preview.6.21355.2 to 6.0.100-rc.1.21455.2 (#16041) (#16028) (#15648)
  • +
  • Fix the GitHub Action for updating .NET daily builds (#16042)
  • +
  • Move from PkgES hosted agents to 1ES hosted agents (#16023)
  • +
  • Update Ubuntu images to use Ubuntu 20.04 (#15906)
  • +
  • Fix the macOS build by updating the pool image name (#16010)
  • +
  • Use Alpine 3.12 for building PowerShell for Alpine Linux (#16008)
  • +
  • Ignore error from Find-Package (#15999)
  • +
  • Find packages separately for each source in UpdateDotnetRuntime.ps1 script (#15998)
  • +
  • Update metadata to start using .NET 6 RC1 builds (#15981)
  • +
+ +
+ +[7.2.0-preview.10]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.9...v7.2.0-preview.10 + +## [7.2.0-preview.9] - 2021-08-23 + +### Breaking Changes + +- Change the default value of `$PSStyle.OutputRendering` to `OutputRendering.Host` and remove `OutputRendering.Automatic` (#15882) +- Fix `CA1052` for public API to make classes static when they only have static methods (#15775) (Thanks @xtqqczze!) +- Update `pwsh.exe -File` to only accept `.ps1` script files on Windows (#15859) + +### Engine Updates and Fixes + +- Update .NET adapter to handle interface static members properly (#15908) +- Catch and handle unauthorized access exception when removing AppLocker test files (#15881) + +### General Cmdlet Updates and Fixes + +- Add `-PassThru` parameter to `Set-Clipboard` (#13713) (Thanks @ThomasNieto!) +- Add `-Encoding` parameter for `Tee-Object` (#12135) (Thanks @Peter-Schneider!) +- Update `ConvertTo-Csv` and `Export-Csv` to handle `IDictionary` objects (#11029) (Thanks @vexx32!) +- Update the parameters `-Exception` and `-ErrorRecord` for `Write-Error` to be position 0 (#13813) (Thanks @ThomasNieto!) +- Don't use `ArgumentList` when creating COM object with `New-Object` as it's not applicable to the COM parameter set (#15915) +- Fix `$PSStyle` list output to correctly show `TableHeader` (#15928) +- Remove the `PSImplicitRemotingBatching` experimental feature (#15863) +- Fix issue with `Get-Process -Module` failing to stop when it's piped to `Select-Object` (#15682) (Thanks @ArmaanMcleod!) +- Make the experimental features `PSUnixFileStat`, `PSCultureInvariantReplaceOperator`, `PSNotApplyErrorActionToStderr`, `PSAnsiRendering`, `PSAnsiProgressFeatureName` stable (#15864) +- Enhance `Remove-Item` to work with OneDrive (#15571) (Thanks @iSazonov!) +- Make global tool entrypoint class static (#15880) +- Update `ServerRemoteHost` version to be same as `PSVersion` (#15809) +- Make the initialization of `HttpKnownHeaderNames` thread safe (#15519) (Thanks @iSazonov!) +- `ConvertTo-Csv`: Quote fields with quotes and newlines when using `-UseQuotes AsNeeded` (#15765) (Thanks @lselden!) +- Forwarding progress stream changes from `Foreach-Object -Parallel` runspaces (#14271) (Thanks @powercode!) +- Add validation to `$PSStyle` to reject printable text when setting a property that only expects ANSI escape sequence (#15825) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze

+ +
+ +
    +
  • Avoid unneeded array allocation in module code (#14329) (Thanks @xtqqczze!)
  • +
  • Enable and fix analysis rules CA1052, CA1067, and IDE0049 (#15840) (Thanks @xtqqczze!)
  • +
  • Avoid unnecessary allocation in formatting code (#15832) (Thanks @xtqqczze!)
  • +
  • Specify the analyzed API surface for all code quality rules (#15778) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Enable `/rebase` to automatically rebase a PR (#15808) +- Update `.editorconfig` to not replace tabs with spaces in `.tsv` files (#15815) (Thanks @SethFalco!) +- Update PowerShell team members in the change log generation script (#15817) + +### Tests + +- Add more tests to validate the current command error handling behaviors (#15919) +- Make `Measure-Object` property test independent of the file system (#15879) +- Add more information when a `syslog` parsing error occurs (#15857) +- Harden logic when looking for `syslog` entries to be sure that we select based on the process id (#15841) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@xtqqczze

+ +
+ +
    +
  • Disable implicit namespace imports for test projects (#15895)
  • +
  • Update language version to 10 and fix related issues (#15886)
  • +
  • Update CodeQL workflow to use Ubuntu 18.04 (#15868)
  • +
  • Bump the version of various packages (#15944, #15934, #15935, #15891, #15812, #15822) (Thanks @xtqqczze!)
  • +
+ +
+ +### Documentation and Help Content + +- Update `README` and `metadata files` for release `v7.2.0-preview.8` (#15819) +- Update change logs for 7.0.7 and 7.1.4 (#15921) +- Fix spelling in XML docs (#15939) (Thanks @slowy07!) +- Update PowerShell Committee members (#15837) + +[7.2.0-preview.9]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.8...v7.2.0-preview.9 + +## [7.2.0-preview.8] - 2021-07-22 + +### Engine Updates and Fixes + +- Add a Windows mode to `$PSNativeCommandArgumentPassing` that allows some commands to use legacy argument passing (#15408) +- Use `nameof` to get parameter names when creating `ArgumentNullException` (#15604) (Thanks @gukoff!) +- Test if a command is 'Out-Default' more thoroughly for transcribing scenarios (#15653) +- Add `Microsoft.PowerShell.Crescendo` to telemetry allow list (#15372) + +### General Cmdlet Updates and Fixes + +- Use `$PSStyle.Formatting.FormatAccent` for `Format-List` and `$PSStyle.Formatting.TableHeader` for `Format-Table` output (#14406) +- Highlight using error color the exception `Message` and underline in `PositionMessage` for `Get-Error` (#15786) +- Implement a completion for View parameter of format cmdlets (#14513) (Thanks @iSazonov!) +- Add support to colorize `FileInfo` file names (#14403) +- Don't serialize to JSON ETS properties for `DateTime` and `string` types (#15665) +- Fix `HyperVSocketEndPoint.ServiceId` setter (#15704) (Thanks @xtqqczze!) +- Add `DetailedView` to `$ErrorView` (#15609) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@iSazonov, @xtqqczze

+ +
+ +
    +
  • Remove consolehost.proto file (#15741) (Thanks @iSazonov!)
  • +
  • Implement IDisposable for ConvertToJsonCommand (#15787) (Thanks @xtqqczze!)
  • +
  • Fix IDisposable implementation for CommandPathSearch (#15793) (Thanks @xtqqczze!)
  • +
  • Delete IDE dispose analyzer rules (#15798) (Thanks @xtqqczze!)
  • +
  • Seal private classes (#15725) (Thanks @xtqqczze!)
  • +
  • Enable IDE0029: UseCoalesceExpression (#15770) (Thanks @xtqqczze!)
  • +
  • Enable IDE0070: UseSystemHashCode (#15715) (Thanks @xtqqczze!)
  • +
  • Enable IDE0030: UseCoalesceExpressionForNullable (#14289) (Thanks @xtqqczze!)
  • +
  • Fix CA1846 and CA1845 for using AsSpan instead of Substring (#15738)
  • +
  • Use List<T>.RemoveAll to avoid creating temporary list (#15686) (Thanks @xtqqczze!)
  • +
  • Enable IDE0044: MakeFieldReadonly (#13880) (Thanks @xtqqczze!)
  • +
  • Disable IDE0130 (#15728) (Thanks @xtqqczze!)
  • +
  • Make classes sealed (#15675) (Thanks @xtqqczze!)
  • +
  • Enable CA1043: Use integral or string argument for indexers (#14467) (Thanks @xtqqczze!)
  • +
  • Enable CA1812 (#15674) (Thanks @xtqqczze!)
  • +
  • Replace Single with First when we know the element count is 1 (#15676) (Thanks @xtqqczze!)
  • +
  • Skip analyzers for Microsoft.Management.UI.Internal (#15677) (Thanks @xtqqczze!)
  • +
  • Fix CA2243: Attribute string literals should parse correctly (#15622) (Thanks @xtqqczze!)
  • +
  • Enable CA1401 (#15621) (Thanks @xtqqczze!)
  • +
  • Fix CA1309: Use ordinal StringComparison in Certificate Provider (#14352) (Thanks @xtqqczze!)
  • +
  • Fix CA1839: Use Environment.ProcessPath (#15650) (Thanks @xtqqczze!)
  • +
  • Add new analyzer rules (#15620) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Add `SkipRoslynAnalyzers` parameter to `Start-PSBuild` (#15640) (Thanks @xtqqczze!) +- Create issue template for issues updating PowerShell through Windows update. (#15700) +- Add `DocumentationAnalyzers` to build (#14336) (Thanks @xtqqczze!) +- Convert GitHub issue templates to modern forms (#15645) + +### Tests + +- Add more tests for `ConvertFrom-Json` (#15706) (Thanks @strawgate!) +- Update `glob-parent` and `hosted-git-info` test dependencies (#15643) + +### Build and Packaging Improvements + +
+ + +Update .NET to version v6.0.0-preview.6 + + +
    +
  • Add new package name for osx-arm64 (#15813)
  • +
  • Prefer version when available for dotnet-install (#15810)
  • +
  • Make warning about MU being required dynamic (#15776)
  • +
  • Add Start-PSBootstrap before running tests (#15804)
  • +
  • Update to .NET 6 Preview 6 and use crossgen2 (#15763)
  • +
  • Enable ARM64 packaging for macOS (#15768)
  • +
  • Make Microsoft Update opt-out/in check boxes work (#15784)
  • +
  • Add Microsoft Update opt out to MSI install (#15727)
  • +
  • Bump NJsonSchema from 10.4.4 to 10.4.5 (#15769)
  • +
  • Fix computation of SHA512 checksum (#15736)
  • +
  • Update the script to use quality parameter for dotnet-install (#15731)
  • +
  • Generate SHA512 checksum file for all packages (#15678)
  • +
  • Enable signing daily release build with lifetime certificate (#15642)
  • +
  • Update metadata and README for 7.2.0-preview.7 (#15593)
  • +
+ +
+ +### Documentation and Help Content + +- Fix broken RFC links (#15807) +- Add to bug report template getting details from `Get-Error` (#15737) +- Update issue templates to link to new docs (#15711) +- Add @jborean93 to Remoting Working Group (#15683) + +[7.2.0-preview.8]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.7...v7.2.0-preview.8 + +## [7.2.0-preview.7] - 2021-06-17 + +### Breaking Changes + +- Remove PSDesiredStateConfiguration v2.0.5 module and published it to the PowerShell Gallery (#15536) + +### Engine Updates and Fixes + +- Fix splatting being treated as positional parameter in completions (#14623) (Thanks @MartinGC94!) +- Prevent PowerShell from crashing when a telemetry mutex can't be created (#15574) (Thanks @gukoff!) +- Ignore all exceptions when disposing an instance of a subsystem implementation (#15511) +- Wait for SSH exit when closing remote connection (#14635) (Thanks @dinhngtu!) + +### Performance + +- Retrieve `ProductVersion` using informational version attribute in `AmsiUtils.Init()` (#15527) (Thanks @Fs00!) + +### General Cmdlet Updates and Fixes + +- Fix retrieving dynamic parameters from provider even if globbed path returns no results (#15525) +- Revert "Enhance Remove-Item to work with OneDrive (#15260)" due to long path issue (#15546) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@octos4murai, @iSazonov, @Fs00

+ +
+ +
    +
  • Correct parameter name passed to exception in PSCommand constructor (#15580) (Thanks @octos4murai!)
  • +
  • Enable nullable: System.Management.Automation.ICommandRuntime (#15566) (Thanks @iSazonov!)
  • +
  • Clean up code regarding AppDomain.CreateDomain and AppDomain.Unload (#15554)
  • +
  • Replace ProcessModule.FileName with Environment.ProcessPath and remove PSUtils.GetMainModule (#15012) (Thanks @Fs00!)
  • +
+ +
+ +### Tests + +- Fix `Start-Benchmarking` to put `TargetPSVersion` and `TargetFramework` in separate parameter sets (#15508) +- Add `win-x86` test package to the build (#15517) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@schuelermine

+ +
+ +
    +
  • Update README.md and metadata.json for version 7.2.0-preview.6 (#15464)
  • +
  • Make sure GA revision increases from RC and Preview releases (#15558)
  • +
  • Remove SupportsShouldProcess from Start-PSBootstrap in build.psm1 (#15491) (Thanks @schuelermine!)
  • +
  • Update DotnetMetadataRuntime.json next channel to take daily build from .NET preview 5 (#15518)
  • +
  • Fix deps.json update in the release pipeline (#15486)
  • +
+ +
+ +### Documentation and Help Content + +- Add new members to Engine and Cmdlet Working Groups document (#15560) +- Update the `mdspell` command to exclude the folder that should be ignored (#15576) +- Replace 'User Voice' with 'Feedback Hub' in `README.md` (#15557) +- Update Virtual User Group chat links (#15505) (Thanks @Jaykul!) +- Fix typo in `FileSystemProvider.cs` (#15445) (Thanks @eltociear!) +- Add `PipelineStoppedException` notes to PowerShell API (#15324) +- Updated governance on Working Groups (WGs) (#14603) +- Correct and improve XML documentation comments on `PSCommand` (#15568) (Thanks @octos4murai!) + +[7.2.0-preview.7]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.6...v7.2.0-preview.7 + +## [7.2.0-preview.6] - 2021-05-27 + +### Experimental Features + +- [Breaking Change] Update prediction interface to provide additional feedback to a predictor plugin (#15421) + +### Performance + +- Avoid collecting logs in buffer if a pipeline execution event is not going to be logged (#15350) +- Avoid allocation in `LanguagePrimitives.UpdateTypeConvertFromTypeTable` (#15168) (Thanks @xtqqczze!) +- Replace `Directory.GetDirectories` with `Directory.EnumerateDirectories` to avoid array allocations (#15167) (Thanks @xtqqczze!) +- Use `List.ConvertAll` instead of `LINQ` (#15140) (Thanks @xtqqczze!) + +### General Cmdlet Updates and Fixes + +- Use `AllocConsole` before initializing CLR to ensure codepage is correct for WinRM remoting (PowerShell/PowerShell-Native#70) (Thanks @jborean93!) +- Add completions for `#requires` statements (#14596) (Thanks @MartinGC94!) +- Add completions for comment-based help keywords (#15337) (Thanks @MartinGC94!) +- Move cross platform DSC code to a PowerShell engine subsystem (#15127) +- Fix `Minimal` progress view to handle activity that is longer than console width (#15264) +- Handle exception if ConsoleHost tries to set cursor out of bounds because screen buffer changed (#15380) +- Fix `NullReferenceException` in DSC `ClearCache()` (#15373) +- Update `ControlSequenceLength` to handle colon as a virtual terminal parameter separator (#14942) +- Update the summary comment for `StopTranscriptCmdlet.cs` (#15349) (Thanks @dbaileyut!) +- Remove the unusable alias `d` for the `-Directory` parameter from `Get-ChildItem` (#15171) (Thanks @kvprasoon!) +- Fix tab completion for un-localized `about` topics (#15265) (Thanks @MartinGC94!) +- Remove the unneeded SSH stdio handle workaround (#15308) +- Add `LoadAssemblyFromNativeMemory` API to load assemblies from memory in a native PowerShell host (#14652) (Thanks @awakecoding!) +- Re-implement `Remove-Item` OneDrive support (#15260) (Thanks @iSazonov!) +- Kill native processes in pipeline when pipeline is disposed on Unix (#15287) +- Default to MTA on Windows platforms where STA is not supported (#15106) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @powercode, @bcwood

+ +
+ +
    +
  • Enable nullable in some classes (#14185, #14177, #14159, #14191, #14162, #14150, #14156, #14161, #14155, #14163, #14181, #14157, #14151) (Thanks @powercode!)
  • +
  • Annotate ThrowTerminatingError with DoesNotReturn attribute (#15352) (Thanks @powercode!)
  • +
  • Use GetValueOrDefault() for nullable PSLanguageMode (#13849) (Thanks @bcwood!)
  • +
  • Enable SA1008: Opening parenthesis should be spaced correctly (#14242) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Add `winget` release script (#15050) + +### Tests + +- Enable cross-runtime benchmarking to compare different .NET runtimes (#15387) (Thanks @adamsitnik!) +- Add the performance benchmark project for PowerShell performance testing (#15242) + +### Build and Packaging Improvements + +
+ + +Update .NET to version v6.0.0-preview.4 + + +
    +
  • Suppress prompting when uploading the msixbundle package to blob (#15227)
  • +
  • Update to .NET preview 4 SDK (#15452)
  • +
  • Update AppxManifest.xml with newer OS version to allow PowerShell installed from Windows Store to make system-level changes (#15375)
  • +
  • Ensure the build works when PSDesiredStateConfiguration module is pulled in from PSGallery (#15355)
  • +
  • Make sure daily release tag does not change when retrying failures (#15286)
  • +
  • Improve messages and behavior when there's a problem in finding zip files (#15284)
  • +
+ +
+ +### Documentation and Help Content + +- Add documentation comments section to coding guidelines (#14316) (Thanks @xtqqczze!) + +[7.2.0-preview.6]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.5...v7.2.0-preview.6 + +## [7.2.0-preview.5] - 2021-04-14 + +### Breaking Changes + +- Make PowerShell Linux deb and RPM packages universal (#15109) +- Enforce AppLocker Deny configuration before Execution Policy Bypass configuration (#15035) +- Disallow mixed dash and slash in command line parameter prefix (#15142) (Thanks @davidBar-On!) + +### Experimental Features + +- `PSNativeCommandArgumentPassing`: Use `ArgumentList` for native executable invocation (breaking change) (#14692) + +### Engine Updates and Fixes + +- Add `IArgumentCompleterFactory` for parameterized `ArgumentCompleters` (#12605) (Thanks @powercode!) + +### General Cmdlet Updates and Fixes + +- Fix SSH remoting connection never finishing with misconfigured endpoint (#15175) +- Respect `TERM` and `NO_COLOR` environment variables for `$PSStyle` rendering (#14969) +- Use `ProgressView.Classic` when Virtual Terminal is not supported (#15048) +- Fix `Get-Counter` issue with `-Computer` parameter (#15166) (Thanks @krishnayalavarthi!) +- Fix redundant iteration while splitting lines (#14851) (Thanks @hez2010!) +- Enhance `Remove-Item -Recurse` to work with OneDrive (#14902) (Thanks @iSazonov!) +- Change minimum depth to 0 for `ConvertTo-Json` (#14830) (Thanks @kvprasoon!) +- Allow `Set-Clipboard` to accept empty string (#14579) +- Turn on and off `DECCKM` to modify keyboard mode for Unix native commands to work correctly (#14943) +- Fall back to `CopyAndDelete()` when `MoveTo()` fails due to an `IOException` (#15077) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @iSazonov, @ZhiZe-ZG

+ +
+ +
    +
  • Update .NET to 6.0.0-preview.3 (#15221)
  • +
  • Add space before comma to hosting test to fix error reported by SA1001 (#15224)
  • +
  • Add SecureStringHelper.FromPlainTextString helper method for efficient secure string creation (#14124) (Thanks @xtqqczze!)
  • +
  • Use static lambda keyword (#15154) (Thanks @iSazonov!)
  • +
  • Remove unnecessary Array -> List -> Array conversion in ProcessBaseCommand.AllProcesses (#15052) (Thanks @xtqqczze!)
  • +
  • Standardize grammar comments in Parser.cs (#15114) (Thanks @ZhiZe-ZG!)
  • +
  • Enable SA1001: Commas should be spaced correctly (#14171) (Thanks @xtqqczze!)
  • +
  • Refactor MultipleServiceCommandBase.AllServices (#15053) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Use Unix line endings for shell scripts (#15180) (Thanks @xtqqczze!) + +### Tests + +- Add the missing tag in Host Utilities tests (#14983) +- Update `copy-props` version in `package.json` (#15124) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@JustinGrote

+ +
+ +
    +
  • Fix yarn-lock for copy-props (#15225)
  • +
  • Make package validation regex accept universal Linux packages (#15226)
  • +
  • Bump NJsonSchema from 10.4.0 to 10.4.1 (#15190)
  • +
  • Make MSI and EXE signing always copy to fix daily build (#15191)
  • +
  • Sign internals of EXE package so that it works correctly when signed (#15132)
  • +
  • Bump Microsoft.NET.Test.Sdk from 16.9.1 to 16.9.4 (#15141)
  • +
  • Update daily release tag format to work with new Microsoft Update work (#15164)
  • +
  • Feature: Add Ubuntu 20.04 Support to install-powershell.sh (#15095) (Thanks @JustinGrote!)
  • +
  • Treat rebuild branches like release branches (#15099)
  • +
  • Update WiX to 3.11.2 (#15097)
  • +
  • Bump NJsonSchema from 10.3.11 to 10.4.0 (#15092)
  • +
  • Allow patching of preview releases (#15074)
  • +
  • Bump Newtonsoft.Json from 12.0.3 to 13.0.1 (#15084, #15085)
  • +
  • Update the minSize build package filter to be explicit (#15055)
  • +
  • Bump NJsonSchema from 10.3.10 to 10.3.11 (#14965)
  • +
+ +
+ +### Documentation and Help Content + +- Merge `7.2.0-preview.4` changes to master (#15056) +- Update `README` and `metadata.json` (#15046) +- Fix broken links for `dotnet` CLI (#14937) + +[7.2.0-preview.5]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.4...v7.2.0-preview.5 + +## [7.2.0-preview.4] - 2021-03-16 + +### Breaking Changes + +- Fix `Get-Date -UFormat` `%G` and `%g` behavior (#14555) (Thanks @brianary!) + +### Engine Updates and Fixes + +- Update engine script signature validation to match `Get-AuthenticodeSignature` logic (#14849) +- Avoid array allocations from `GetDirectories` and `GetFiles` (#14327) (Thanks @xtqqczze!) + +### General Cmdlet Updates and Fixes + +- Add `UseOSCIndicator` setting to enable progress indicator in terminal (#14927) +- Re-enable VT mode on Windows after running command in `ConsoleHost` (#14413) +- Fix `Move-Item` for `FileSystemProvider` to use copy-delete instead of move for DFS paths (#14913) +- Fix `PromptForCredential()` to add `targetName` as domain (#14504) +- Update `Concise` `ErrorView` to not show line information for errors from script module functions (#14912) +- Remove the 32,767 character limit on the environment block for `Start-Process` (#14111) (Thanks @hbuckle!) +- Don't write possible secrets to verbose stream for web cmdlets (#14788) + +### Tools + +- Update `dependabot` configuration to V2 format (#14882) +- Add tooling issue slots in PR template (#14697) + +### Tests + +- Move misplaced test file to tests directory (#14908) (Thanks @MarianoAlipi!) +- Refactor MSI CI (#14753) + +### Build and Packaging Improvements + +
+ + +Update .NET to version 6.0.100-preview.2.21155.3 + + +
    +
  • Update .NET to version 6.0.100-preview.2.21155.3 (#15007)
  • +
  • Bump Microsoft.PowerShell.Native to 7.2.0-preview.1 (#15030)
  • +
  • Create MSIX Bundle package in release pipeline (#14982)
  • +
  • Build self-contained minimal size package for Guest Config team (#14976)
  • +
  • Bump XunitXml.TestLogger from 3.0.62 to 3.0.66 (#14993) (Thanks @dependabot[bot]!)
  • +
  • Enable building PowerShell for Apple M1 runtime (#14923)
  • +
  • Fix the variable name in the condition for miscellaneous analysis CI (#14975)
  • +
  • Fix the variable usage in CI yaml (#14974)
  • +
  • Disable running markdown link verification in release build CI (#14971)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 3.9.0-3.final to 3.9.0 (#14934) (Thanks @dependabot[bot]!)
  • +
  • Declare which variable group is used for checking the blob in the release build (#14970)
  • +
  • Update metadata and script to enable consuming .NET daily builds (#14940)
  • +
  • Bump NJsonSchema from 10.3.9 to 10.3.10 (#14933) (Thanks @dependabot[bot]!)
  • +
  • Use template that disables component governance for CI (#14938)
  • +
  • Add suppress for nuget multi-feed warning (#14893)
  • +
  • Bump NJsonSchema from 10.3.8 to 10.3.9 (#14926) (Thanks @dependabot[bot]!)
  • +
  • Add exe wrapper to release (#14881)
  • +
  • Bump Microsoft.ApplicationInsights from 2.16.0 to 2.17.0 (#14847)
  • +
  • Bump Microsoft.NET.Test.Sdk from 16.8.3 to 16.9.1 (#14895) (Thanks @dependabot[bot]!)
  • +
  • Bump NJsonSchema from 10.3.7 to 10.3.8 (#14896) (Thanks @dependabot[bot]!)
  • +
  • Disable codesign validation where the file type is not supported (#14885)
  • +
  • Fixing broken Experimental Feature list in powershell.config.json (#14858)
  • +
  • Bump NJsonSchema from 10.3.6 to 10.3.7 (#14855)
  • +
  • Add exe wrapper for Microsoft Update scenarios (#14737)
  • +
  • Install wget on CentOS 7 docker image (#14857)
  • +
  • Fix install-dotnet download (#14856)
  • +
  • Fix Bootstrap step in Windows daily test runs (#14820)
  • +
  • Bump NJsonSchema from 10.3.5 to 10.3.6 (#14818)
  • +
  • Bump NJsonSchema from 10.3.4 to 10.3.5 (#14807)
  • +
+ +
+ +### Documentation and Help Content + +- Update `README.md` and `metadata.json` for upcoming releases (#14755) +- Merge 7.1.3 and 7.0.6 Change log to master (#15009) +- Update `README` and `metadata.json` for releases (#14997) +- Update ChangeLog for `v7.1.2` release (#14783) +- Update ChangeLog for `v7.0.5` release (#14782) (Internal 14479) + +[7.2.0-preview.4]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.3...v7.2.0-preview.4 + +## [7.2.0-preview.3] - 2021-02-11 + +### Breaking Changes + +- Fix `Get-Date -UFormat %u` behavior to comply with ISO 8601 (#14549) (Thanks @brianary!) + +### Engine Updates and Fixes + +- Together with `PSDesiredStateConfiguration` `v3` module allows `Get-DscResource`, `Invoke-DscResource` and DSC configuration compilation on all platforms, supported by PowerShell (using class-based DSC resources). + +### Performance + +- Avoid array allocations from `Directory.GetDirectories` and `Directory.GetFiles`. (#14326) (Thanks @xtqqczze!) +- Avoid `string.ToLowerInvariant()` from `GetEnvironmentVariableAsBool()` to avoid loading libicu at startup (#14323) (Thanks @iSazonov!) +- Get PowerShell version in `PSVersionInfo` using assembly attribute instead of `FileVersionInfo` (#14332) (Thanks @Fs00!) + +### General Cmdlet Updates and Fixes + +- Suppress `Write-Progress` in `ConsoleHost` if output is redirected and fix tests (#14716) +- Experimental feature `PSAnsiProgress`: Add minimal progress bar using ANSI rendering (#14414) +- Fix web cmdlets to properly construct URI from body when using `-NoProxy` (#14673) +- Update the `ICommandPredictor` to provide more feedback and also make feedback easier to be correlated (#14649) +- Reset color after writing `Verbose`, `Debug`, and `Warning` messages (#14698) +- Fix using variable for nested `ForEach-Object -Parallel` calls (#14548) +- When formatting, if collection is modified, don't fail the entire pipeline (#14438) +- Improve completion of parameters for attributes (#14525) (Thanks @MartinGC94!) +- Write proper error messages for `Get-Command ' '` (#13564) (Thanks @jakekerr!) +- Fix typo in the resource string `ProxyURINotSupplied` (#14526) (Thanks @romero126!) +- Add support to `$PSStyle` for strikethrough and hyperlinks (#14461) +- Fix `$PSStyle` blink codes (#14447) (Thanks @iSazonov!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @powercode

+ +
+ +
    +
  • Fix coding style issues: RCS1215, IDE0090, SA1504, SA1119, RCS1139, IDE0032 (#14356, #14341, #14241, #14204, #14442, #14443) (Thanks @xtqqczze!)
  • +
  • Enable coding style checks: CA2249, CA1052, IDE0076, IDE0077, SA1205, SA1003, SA1314, SA1216, SA1217, SA1213 (#14395, #14483, #14494, #14495, #14441, #14476, #14470, #14471, #14472) (Thanks @xtqqczze!)
  • +
  • Enable nullable in PowerShell codebase (#14160, #14172, #14088, #14154, #14166, #14184, #14178) (Thanks @powercode!)
  • +
  • Use string.Split(char) instead of string.Split(string) (#14465) (Thanks @xtqqczze!)
  • +
  • Use string.Contains(char) overload (#14368) (Thanks @xtqqczze!)
  • +
  • Refactor complex if statements (#14398) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Update script to use .NET 6 build resources (#14705) +- Fix the daily GitHub action (#14711) (Thanks @imba-tjd!) +- GitHub Actions: fix deprecated `::set-env` (#14629) (Thanks @imba-tjd!) +- Update markdown test tools (#14325) (Thanks @RDIL!) +- Upgrade `StyleCopAnalyzers` to `v1.2.0-beta.312` (#14354) (Thanks @xtqqczze!) + +### Tests + +- Remove packaging from daily Windows build (#14749) +- Update link to the Manning book (#14750) +- A separate Windows packaging CI (#14670) +- Update `ini` component version in test `package.json` (#14454) +- Disable `libmi` dependent tests for macOS. (#14446) + +### Build and Packaging Improvements + +
+ +
    +
  • Fix the NuGet feed name and URL for .NET 6
  • +
  • Fix third party signing for files in sub-folders (#14751)
  • +
  • Make build script variable an ArrayList to enable Add() method (#14748)
  • +
  • Remove old .NET SDKs to make dotnet restore work with the latest SDK in CI pipeline (#14746)
  • +
  • Remove outdated Linux dependencies (#14688)
  • +
  • Bump .NET SDK version to 6.0.0-preview.1 (#14719)
  • +
  • Bump NJsonSchema to 10.3.4 (#14714)
  • +
  • Update daily GitHub action to allow manual trigger (#14718)
  • +
  • Bump XunitXml.TestLogger to 3.0.62 (#14702)
  • +
  • Make universal deb package based on the deb package specification (#14681)
  • +
  • Add manual release automation steps and improve changelog script (#14445)
  • +
  • Fix release build to upload global tool packages to artifacts (#14620)
  • +
  • Port changes from the PowerShell v7.0.4 release (#14637)
  • +
  • Port changes from the PowerShell v7.1.1 release (#14621)
  • +
  • Updated README and metadata.json (#14401, #14606, #14612)
  • +
  • Do not push nupkg artifacts to MyGet (#14613)
  • +
  • Use one feed in each nuget.config in official builds (#14363)
  • +
  • Fix path signed RPMs are uploaded from in release build (#14424)
  • +
+ +
+ +### Documentation and Help Content + +- Update distribution support request template to point to .NET 5.0 support document (#14578) +- Remove security GitHub issue template (#14453) +- Add intent for using the Discussions feature in repo (#14399) +- Fix Universal Dashboard to refer to PowerShell Universal (#14437) +- Update document link because of HTTP 301 redirect (#14431) (Thanks @xtqqczze!) + +[7.2.0-preview.3]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.2...v7.2.0-preview.3 + +## [7.2.0-preview.2] - 2020-12-15 + +### Breaking Changes + +- Improve detection of mutable value types (#12495) (Thanks @vexx32!) +- Ensure `-PipelineVariable` is set for all output from script cmdlets (#12766) (Thanks @vexx32!) + +### Experimental Features + +- `PSAnsiRendering`: Enable ANSI formatting via `$PSStyle` and support suppressing ANSI output (#13758) + +### Performance + +- Optimize `IEnumerable` variant of replace operator (#14221) (Thanks @iSazonov!) +- Refactor multiply operation for better performance in two `Microsoft.PowerShell.Commands.Utility` methods (#14148) (Thanks @xtqqczze!) +- Use `Environment.TickCount64` instead of `Datetime.Now` as the random seed for AppLocker test file content (#14283) (Thanks @iSazonov!) +- Avoid unnecessary array allocations when searching in GAC (#14291) (Thanks @xtqqczze!) +- Use `OrdinalIgnoreCase` in `CommandLineParser` (#14303) (Thanks @iSazonov!) +- Use `StringComparison.Ordinal` instead of `StringComparison.CurrentCulture` (#14298) (Thanks @iSazonov!) +- Avoid creating instances of the generated delegate helper class in `-replace` implementation (#14128) + +### General Cmdlet Updates and Fixes + +- Write better error message if config file is broken (#13496) (Thanks @iSazonov!) +- Make AppLocker Enforce mode take precedence over UMCI Audit mode (#14353) +- Add `-SkipLimitCheck` switch to `Import-PowerShellDataFile` (#13672) +- Restrict `New-Object` in NoLanguage mode under lock down (#14140) (Thanks @krishnayalavarthi!) +- The `-Stream` parameter now works with directories (#13941) (Thanks @kyanha!) +- Avoid an exception if file system does not support reparse points (#13634) (Thanks @iSazonov!) +- Enable `CA1012`: Abstract types should not have public constructors (#13940) (Thanks @xtqqczze!) +- Enable `SA1212`: Property accessors should follow order (#14051) (Thanks @xtqqczze!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @matthewjdegarmo, @powercode, @Gimly

+ +
+ +
    +
  • Enable SA1007: Operator keyword should be followed by space (#14130) (Thanks @xtqqczze!)
  • +
  • Expand where alias to Where-Object in Reset-PWSHSystemPath.ps1 (#14113) (Thanks @matthewjdegarmo!)
  • +
  • Fix whitespace issues (#14092) (Thanks @xtqqczze!)
  • +
  • Add StyleCop.Analyzers package (#13963) (Thanks @xtqqczze!)
  • +
  • Enable IDE0041: UseIsNullCheck (#14041) (Thanks @xtqqczze!)
  • +
  • Enable IDE0082: ConvertTypeOfToNameOf (#14042) (Thanks @xtqqczze!)
  • +
  • Remove unnecessary usings part 4 (#14023) (Thanks @xtqqczze!)
  • +
  • Fix PriorityAttribute name (#14094) (Thanks @xtqqczze!)
  • +
  • Enable nullable: System.Management.Automation.Interpreter.IBoxableInstruction (#14165) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.Provider.IDynamicPropertyProvider (#14167) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.Language.IScriptExtent (#14179) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.Language.ICustomAstVisitor2 (#14192) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.LanguagePrimitives.IConversionData (#14187) (Thanks @powercode!)
  • +
  • Enable nullable: System.Automation.Remoting.Client.IWSManNativeApiFacade (#14186) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.Language.ISupportsAssignment (#14180) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.ICommandRuntime2 (#14183) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.IOutputProcessingState (#14175) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.IJobDebugger (#14174) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.Interpreter.IInstructionProvider (#14173) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.IHasSessionStateEntryVisibility (#14169) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.Tracing.IEtwEventCorrelator (#14168) (Thanks @powercode!)
  • +
  • Fix syntax error in Windows packaging script (#14377)
  • +
  • Remove redundant local assignment in AclCommands (#14358) (Thanks @xtqqczze!)
  • +
  • Enable nullable: System.Management.Automation.Language.IAstPostVisitHandler (#14164) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.IModuleAssemblyInitializer (#14158) (Thanks @powercode!)
  • +
  • Use Microsoft.PowerShell.MarkdownRender package from nuget.org (#14090)
  • +
  • Replace GetFiles in TestModuleManifestCommand (#14317) (Thanks @xtqqczze!)
  • +
  • Enable nullable: System.Management.Automation.Provider.IContentWriter (#14152) (Thanks @powercode!)
  • +
  • Simplify getting Encoding in TranscriptionOption.FlushContentToDisk (#13910) (Thanks @Gimly!)
  • +
  • Mark applicable structs as readonly and use in-modifier (#13919) (Thanks @xtqqczze!)
  • +
  • Enable nullable: System.Management.Automation.IArgumentCompleter (#14182) (Thanks @powercode!)
  • +
  • Enable CA1822: Mark private members as static (#13897) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 6 (#14338) (Thanks @xtqqczze!)
  • +
  • Avoid array allocations from GetDirectories/GetFiles. (#14328) (Thanks @xtqqczze!)
  • +
  • Avoid array allocations from GetDirectories/GetFiles. (#14330) (Thanks @xtqqczze!)
  • +
  • Fix RCS1188: Remove redundant auto-property initialization part 2 (#14262) (Thanks @xtqqczze!)
  • +
  • Enable nullable: System.Management.Automation.Host.IHostSupportsInteractiveSession (#14170) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.Provider.IPropertyCmdletProvider (#14176) (Thanks @powercode!)
  • +
  • Fix IDE0090: Simplify new expression part 5 (#14301) (Thanks @xtqqczze!)
  • +
  • Enable IDE0075: SimplifyConditionalExpression (#14078) (Thanks @xtqqczze!)
  • +
  • Remove unnecessary usings part 9 (#14288) (Thanks @xtqqczze!)
  • +
  • Fix StyleCop and MarkdownLint CI failures (#14297) (Thanks @xtqqczze!)
  • +
  • Enable SA1000: Keywords should be spaced correctly (#13973) (Thanks @xtqqczze!)
  • +
  • Fix RCS1188: Remove redundant auto-property initialization part 1 (#14261) (Thanks @xtqqczze!)
  • +
  • Mark private members as static part 10 (#14235) (Thanks @xtqqczze!)
  • +
  • Mark private members as static part 9 (#14234) (Thanks @xtqqczze!)
  • +
  • Fix SA1642 for Microsoft.Management.Infrastructure.CimCmdlets (#14239) (Thanks @xtqqczze!)
  • +
  • Use AsSpan/AsMemory slice constructor (#14265) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 4.6 (#14260) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 4.5 (#14259) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 4.3 (#14257) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 4.2 (#14256) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 2 (#14200) (Thanks @xtqqczze!)
  • +
  • Enable SA1643: Destructor summary documentation should begin with standard text (#14236) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 4.4 (#14258) (Thanks @xtqqczze!)
  • +
  • Use xml documentation child blocks correctly (#14249) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 4.1 (#14255) (Thanks @xtqqczze!)
  • +
  • Use consistent spacing in xml documentation tags (#14231) (Thanks @xtqqczze!)
  • +
  • Enable IDE0074: Use coalesce compound assignment (#13396) (Thanks @xtqqczze!)
  • +
  • Remove unnecessary finalizers (#14248) (Thanks @xtqqczze!)
  • +
  • Mark local variable as const (#13217) (Thanks @xtqqczze!)
  • +
  • Fix IDE0032: UseAutoProperty part 2 (#14244) (Thanks @xtqqczze!)
  • +
  • Fix IDE0032: UseAutoProperty part 1 (#14243) (Thanks @xtqqczze!)
  • +
  • Mark private members as static part 8 (#14233) (Thanks @xtqqczze!)
  • +
  • Fix CA1822: Mark members as static part 6 (#14229) (Thanks @xtqqczze!)
  • +
  • Fix CA1822: Mark members as static part 5 (#14228) (Thanks @xtqqczze!)
  • +
  • Fix CA1822: Mark members as static part 4 (#14227) (Thanks @xtqqczze!)
  • +
  • Fix CA1822: Mark members as static part 3 (#14226) (Thanks @xtqqczze!)
  • +
  • Fix CA1822: Mark members as static part 2 (#14225) (Thanks @xtqqczze!)
  • +
  • Fix CA1822: Mark members as static part 1 (#14224) (Thanks @xtqqczze!)
  • +
  • Use see keyword in documentation (#14220) (Thanks @xtqqczze!)
  • +
  • Enable CA2211: Non-constant fields should not be visible (#14073) (Thanks @xtqqczze!)
  • +
  • Enable CA1816: Dispose methods should call SuppressFinalize (#14074) (Thanks @xtqqczze!)
  • +
  • Remove incorrectly implemented finalizer (#14246) (Thanks @xtqqczze!)
  • +
  • Fix CA1822: Mark members as static part 7 (#14230) (Thanks @xtqqczze!)
  • +
  • Fix SA1122: Use string.Empty for empty strings (#14218) (Thanks @xtqqczze!)
  • +
  • Fix various xml documentation issues (#14223) (Thanks @xtqqczze!)
  • +
  • Remove unnecessary usings part 8 (#14072) (Thanks @xtqqczze!)
  • +
  • Enable SA1006: Preprocessor keywords should not be preceded by space (#14052) (Thanks @xtqqczze!)
  • +
  • Fix SA1642 for Microsoft.PowerShell.Commands.Utility (#14142) (Thanks @xtqqczze!)
  • +
  • Enable CA2216: Disposable types should declare finalizer (#14089) (Thanks @xtqqczze!)
  • +
  • Wrap and name LoadBinaryModule arguments (#14193) (Thanks @xtqqczze!)
  • +
  • Wrap and name GetListOfFilesFromData arguments (#14194) (Thanks @xtqqczze!)
  • +
  • Enable SA1002: Semicolons should be spaced correctly (#14197) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 3 (#14201) (Thanks @xtqqczze!)
  • +
  • Enable SA1106: Code should not contain empty statements (#13964) (Thanks @xtqqczze!)
  • +
  • Code performance fixes follow-up (#14207) (Thanks @xtqqczze!)
  • +
  • Remove uninformative comments (#14199) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 1 (#14027) (Thanks @xtqqczze!)
  • +
  • Enable SA1517: Code should not contain blank lines at start of file (#14131) (Thanks @xtqqczze!)
  • +
  • Enable SA1131: Use readable conditions (#14132) (Thanks @xtqqczze!)
  • +
  • Enable SA1507: Code should not contain multiple blank lines in a row (#14136) (Thanks @xtqqczze!)
  • +
  • Enable SA1516 Elements should be separated by blank line (#14137) (Thanks @xtqqczze!)
  • +
  • Enable IDE0031: Null check can be simplified (#13548) (Thanks @xtqqczze!)
  • +
  • Enable CA1065: Do not raise exceptions in unexpected locations (#14117) (Thanks @xtqqczze!)
  • +
  • Enable CA1000: Do not declare static members on generic types (#14097) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Fixing formatting in `Reset-PWSHSystemPath.ps1` (#13689) (Thanks @dgoldman-msft!) + +### Tests + +- Reinstate `Test-Connection` tests (#13324) +- Update markdown test packages with security fixes (#14145) + +### Build and Packaging Improvements + +
+ +
    +
  • Fix a typo in the Get-ChangeLog function (#14129)
  • +
  • Update README and metadata.json for 7.2.0-preview.1 release (#14104)
  • +
  • Bump NJsonSchema from 10.2.2 to 10.3.1 (#14040)
  • +
  • Move windows package signing to use ESRP (#14060)
  • +
  • Use one feed in each nuget.config in official builds (#14363)
  • +
  • Fix path signed RPMs are uploaded from in release build (#14424)
  • +
  • Add Microsoft.PowerShell.MarkdownRender to the package reference list (#14386)
  • +
  • Fix issue with unsigned build (#14367)
  • +
  • Move macOS and nuget to ESRP signing (#14324)
  • +
  • Fix nuget packaging to scrub NullableAttribute (#14344)
  • +
  • Bump Microsoft.NET.Test.Sdk from 16.8.0 to 16.8.3 (#14310)
  • +
  • Bump Markdig.Signed from 0.22.0 to 0.22.1 (#14305)
  • +
  • Bump Microsoft.ApplicationInsights from 2.15.0 to 2.16.0 (#14031)
  • +
  • Move Linux to ESRP signing (#14210)
  • +
+ +
+ +### Documentation and Help Content + +- Fix example `nuget.config` (#14349) +- Fix a broken link in Code Guidelines doc (#14314) (Thanks @iSazonov!) + +[7.2.0-preview.2]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.1...v7.2.0-preview.2 + +## [7.2.0-preview.1] - 2020-11-17 + +### Engine Updates and Fixes + +- Change the default fallback encoding for `GetEncoding` in `Start-Transcript` to be `UTF8` without a BOM (#13732) (Thanks @Gimly!) + +### General Cmdlet Updates and Fixes + +- Update `pwsh -?` output to match docs (#13748) +- Fix `NullReferenceException` in `Test-Json` (#12942) (Thanks @iSazonov!) +- Make `Dispose` in `TranscriptionOption` idempotent (#13839) (Thanks @krishnayalavarthi!) +- Add additional Microsoft PowerShell modules to the tracked modules list (#12183) +- Relax further `SSL` verification checks for `WSMan` on non-Windows hosts with verification available (#13786) (Thanks @jborean93!) +- Add the `OutputTypeAttribute` to `Get-ExperimentalFeature` (#13738) (Thanks @ThomasNieto!) +- Fix blocking wait when starting file associated with a Windows application (#13750) +- Emit warning if `ConvertTo-Json` exceeds `-Depth` value (#13692) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @mkswd, @ThomasNieto, @PatLeong, @paul-cheung, @georgettica

+ +
+ +
    +
  • Fix RCS1049: Simplify boolean comparison (#13994) (Thanks @xtqqczze!)
  • +
  • Enable IDE0062: Make local function static (#14044) (Thanks @xtqqczze!)
  • +
  • Enable CA2207: Initialize value type static fields inline (#14068) (Thanks @xtqqczze!)
  • +
  • Enable CA1837: Use ProcessId and CurrentManagedThreadId from System.Environment (#14063) (Thanks @xtqqczze and @PatLeong!)
  • +
  • Remove unnecessary using directives (#14014, #14017, #14021, #14050, #14065, #14066, #13863, #13860, #13861, #13814) (Thanks @xtqqczze and @ThomasNieto!)
  • +
  • Remove unnecessary usage of LINQ Count method (#13545) (Thanks @xtqqczze!)
  • +
  • Fix SA1518: The code must not contain extra blank lines at the end of the file (#13574) (Thanks @xtqqczze!)
  • +
  • Enable CA1829: Use the Length or Count property instead of Count() (#13925) (Thanks @xtqqczze!)
  • +
  • Enable CA1827: Do not use Count() or LongCount() when Any() can be used (#13923) (Thanks @xtqqczze!)
  • +
  • Enable or fix nullable usage in a few files (#13793, #13805, #13808, #14018, #13804) (Thanks @mkswd and @georgettica!)
  • +
  • Enable IDE0040: Add accessibility modifiers (#13962, #13874) (Thanks @xtqqczze!)
  • +
  • Make applicable private Guid fields readonly (#14000) (Thanks @xtqqczze!)
  • +
  • Fix CA1003: Use generic event handler instances (#13937) (Thanks @xtqqczze!)
  • +
  • Simplify delegate creation (#13578) (Thanks @xtqqczze!)
  • +
  • Fix RCS1033: Remove redundant boolean literal (#13454) (Thanks @xtqqczze!)
  • +
  • Fix RCS1221: Use pattern matching instead of combination of as operator and null check (#13333) (Thanks @xtqqczze!)
  • +
  • Use is not syntax (#13338) (Thanks @xtqqczze!)
  • +
  • Replace magic number with constant in PDH (#13536) (Thanks @xtqqczze!)
  • +
  • Fix accessor order (#13538) (Thanks @xtqqczze!)
  • +
  • Enable IDE0054: Use compound assignment (#13546) (Thanks @xtqqczze!)
  • +
  • Fix RCS1098: Constant values should be on right side of comparisons (#13833) (Thanks @xtqqczze!)
  • +
  • Enable CA1068: CancellationToken parameters must come last (#13867) (Thanks @xtqqczze!)
  • +
  • Enable CA10XX rules with suggestion severity (#13870, #13928, #13924) (Thanks @xtqqczze!)
  • +
  • Enable IDE0064: Make Struct fields writable (#13945) (Thanks @xtqqczze!)
  • +
  • Run dotnet-format to improve formatting of source code (#13503) (Thanks @xtqqczze!)
  • +
  • Enable CA1825: Avoid zero-length array allocations (#13961) (Thanks @xtqqczze!)
  • +
  • Add IDE analyzer rule IDs to comments (#13960) (Thanks @xtqqczze!)
  • +
  • Enable CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder (#13926) (Thanks @xtqqczze!)
  • +
  • Enforce code style in build (#13957) (Thanks @xtqqczze!)
  • +
  • Enable CA1836: Prefer IsEmpty over Count when available (#13877) (Thanks @xtqqczze!)
  • +
  • Enable CA1834: Consider using StringBuilder.Append(char) when applicable (#13878) (Thanks @xtqqczze!)
  • +
  • Fix IDE0044: Make field readonly (#13884, #13885, #13888, #13892, #13889, #13886, #13890, #13891, #13887, #13893, #13969, #13967, #13968, #13970, #13971, #13966, #14012) (Thanks @xtqqczze!)
  • +
  • Enable IDE0048: Add required parentheses (#13896) (Thanks @xtqqczze!)
  • +
  • Enable IDE1005: Invoke delegate with conditional access (#13911) (Thanks @xtqqczze!)
  • +
  • Enable IDE0036: Enable the check on the order of modifiers (#13958, #13881) (Thanks @xtqqczze!)
  • +
  • Use span-based String.Concat instead of String.Substring (#13500) (Thanks @xtqqczze!)
  • +
  • Enable CA1050: Declare types in namespace (#13872) (Thanks @xtqqczze!)
  • +
  • Fix minor keyword typo in C# code comment (#13811) (Thanks @paul-cheung!)
  • +
+ +
+ +### Tools + +- Enable `CodeQL` Security scanning (#13894) +- Add global `AnalyzerConfig` with default configuration (#13835) (Thanks @xtqqczze!) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@mkswd, @xtqqczze

+ +
+ +
    +
  • Bump Microsoft.NET.Test.Sdk to 16.8.0 (#14020)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp to 3.8.0 (#14075)
  • +
  • Remove workarounds for .NET 5 RTM builds (#14038)
  • +
  • Migrate 3rd party signing to ESRP (#14010)
  • +
  • Fixes to release pipeline for GA release (#14034)
  • +
  • Don't do a shallow checkout (#13992)
  • +
  • Add validation and dependencies for Ubuntu 20.04 distribution to packaging script (#13993)
  • +
  • Add .NET install workaround for RTM (#13991)
  • +
  • Move to ESRP signing for Windows files (#13988)
  • +
  • Update PSReadLine version to 2.1.0 (#13975)
  • +
  • Bump .NET to version 5.0.100-rtm.20526.5 (#13920)
  • +
  • Update script to use .NET RTM feeds (#13927)
  • +
  • Add checkout step to release build templates (#13840)
  • +
  • Turn on /features:strict for all projects (#13383) (Thanks @xtqqczze!)
  • +
  • Bump NJsonSchema to 10.2.2 (#13722, #13751)
  • +
  • Add flag to make Linux script publish to production repo (#13714)
  • +
  • Bump Markdig.Signed to 0.22.0 (#13741)
  • +
  • Use new release script for Linux packages (#13705)
  • +
+ +
+ +### Documentation and Help Content + +- Fix links to LTS versions for Windows (#14070) +- Fix `crontab` formatting in example doc (#13712) (Thanks @dgoldman-msft!) + +[7.2.0-preview.1]: https://github.com/PowerShell/PowerShell/compare/v7.1.0...v7.2.0-preview.1 diff --git a/CHANGELOG/7.3.md b/CHANGELOG/7.3.md new file mode 100644 index 00000000000..57c1e3b99f3 --- /dev/null +++ b/CHANGELOG/7.3.md @@ -0,0 +1,1031 @@ +# 7.3 Changelog + +## [7.3.3] - 2023-02-23 + +### Build and Packaging Improvements + +
+ + + +

Bump to use .NET 7.0.3

+ +
+ +
    +
  • Update third party notices for v7.3.3 (Internal 24353)
  • +
  • Add tool to trigger license information gathering for NuGet modules (#18827)
  • +
  • Update global.json to 7.0.200 for v7.3.3 (Internal 24334)
  • +
  • Update cgmanifest for v7.3.3 (Internal 24338)
  • +
+ +
+ +[7.3.3]: https://github.com/PowerShell/PowerShell/compare/v7.3.2...v7.3.3 + +## [7.3.2] - 2023-01-24 + +### Engine Updates and Fixes + +- Fix `SuspiciousContentChecker.Match` to detect a pre-defined string when the text starts with it (#18916) +- Fix for JEA session leaking functions (Internal 23820) + +### General Cmdlet Updates and Fixes + +- Fix `Start-Job` to check the existence of working directory using the PowerShell way (#18917) +- Fix `Switch-Process` error to include the command that is not found (#18650) + +### Tests + +- Allow system lock down test debug hook to work with new `WLDP` API (fixes system lock down tests) (#18962) + +### Build and Packaging Improvements + +
+ + + +

Bump to use .NET 7.0.2

+ +
+ +
    +
  • Update dependencies for .NET release (Internal 23818)
  • +
  • Remove unnecessary reference to System.Runtime.CompilerServices.Unsafe (#18918)
  • +
  • Add bootstrap after SBOM task to re-install .NET (#18891)
  • +
+ +
+ +[7.3.2]: https://github.com/PowerShell/PowerShell/compare/v7.3.1...v7.3.2 + +## [7.3.1] - 2022-12-13 + +### Engine Updates and Fixes + +- Remove TabExpansion for PSv2 from remote session configuration (Internal 23331) +- Add `sqlcmd` to list to use legacy argument passing (#18645 #18646) +- Change `exec` from alias to function to handle arbitrary args (#18644) +- Fix `Switch-Process` to copy the current env to the new process (#18632) +- Fix issue when completing the first command in a script with an empty array expression (#18355) +- Fix `Switch-Process` to set `termios` appropriate for child process (#18572) +- Fix native access violation (#18571) + +### Tests + +- Backport CI fixed from #18508 (#18626) +- Mark charset test as pending (#18609) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+ +
+ +
    +
  • Update packages (Internal 23330)
  • +
  • Apply expected file permissions to linux files after authenticode signing (#18647)
  • +
  • Bump System.Data.SqlClient (#18573)
  • +
  • Don't install based on build-id for RPM (#18570)
  • +
  • Work around args parsing issue (#18607)
  • +
  • Fix package download in vPack job
  • +
+ +
+ +[7.3.1]: https://github.com/PowerShell/PowerShell/compare/v7.3.0...v7.3.1 + +## [7.3.0] - 2022-11-08 + +### General Cmdlet Updates and Fixes + +- Correct calling cmdlet `New-PSSessionOption` in script for `Restart-Computer` (#18374) + +### Tests + +- Add test for framework dependent package in release pipeline (Internal 23139) + +### Build and Packaging Improvements + +
+ + + +

Bump to use internal .NET 7 GA build (Internal 23096)

+ +
+ +
    +
  • Fix issues with building test artifacts (Internal 23116)
  • +
  • Use AzFileCopy task instead of AzCopy.exe
  • +
  • Remove AzCopy installation from msixbundle step
  • +
  • Add TSAUpload for APIScan (#18446)
  • +
  • Add authenticode signing for assemblies on Linux builds (#18440)
  • +
  • Do not remove penimc_cor3.dll from build (#18438)
  • +
  • Allow two-digit revisions in vPack package validation pattern (#18392)
  • +
  • Bump Microsoft.PowerShell.Native from 7.3.0-rc.1 to 7.3.0 (#18413)
  • +
+ +
+ +[7.3.0]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-rc.1...v7.3.0 + +## [7.3.0-rc.1] - 2022-10-26 + +### Breaking Change + +- Update to use `ComputeCore.dll` for PowerShell Direct (#18194) + +### Engine Updates and Fixes + +- On Unix, explicitly terminate the native process during cleanup only if it's not running in background (#18215) + +### General Cmdlet Updates and Fixes + +- Remove the `ProcessorArchitecture` portion from the full name as it's obsolete (#18320) + +### Tests + +- Add missing `-Tag 'CI'` to describe blocks. (#18317) + +### Build and Packaging Improvements + +
+ + +

Bump to .NET 7 to 7.0.100-rc.2.22477.20 (#18328)(#18286)

+
+ +
    +
  • Update ThirdPartyNotices (Internal 22987)
  • +
  • Remove API sets (#18304) (#18376)
  • +
  • Do not cleanup pwsh.deps.json for framework dependent packages (#18300)
  • +
  • Bump Microsoft.PowerShell.Native from 7.3.0-preview.1 to 7.3.0-rc.1 (#18217)
  • +
  • Remove unnecessary native dependencies from the package (#18213)
  • +
  • Make the link to minimal package blob public during release (#18158)
  • +
  • Create tasks to collect and publish hashes for build files. (#18276)(#18277)
  • +
  • Add branch counter to compliance build (#18214)
  • +
  • Move APIScan to compliance build (#18191)
  • +
  • Update MSI exit message (#18137)
  • +
  • Remove XML files for min-size package (#18189)
  • +
+ +
+ +[7.3.0-rc.1]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.8...v7.3.0-rc.1 + +## [7.3.0-preview.8] - 2022-09-20 + +### General Cmdlet Updates and Fixes + +- Filter out compiler generated types for `Add-Type -PassThru` (#18095) +- Fix error formatting to use color defined in `$PSStyle.Formatting` (#17987) +- Handle `PSObject` argument specially in method invocation logging (#18060) +- Revert the experimental feature `PSStrictModeAssignment` (#18040) +- Make experimental feature `PSAMSIMethodInvocationLogging` stable (#18041) +- Make experimental feature `PSAnsiRenderingFileInfo` stable (#18042) +- Make experimental feature `PSCleanBlock` stable (#18043) +- Make experimental feature `PSNativeCommandArgumentPassing` stable (#18044) +- Make experimental feature `PSExec` stable (#18045) +- Make experimental feature `PSRemotingSSHTransportErrorHandling` stable (#18046) +- Add the `ConfigurationFile` option to the PowerShell help content (#18093) + +### Build and Packaging Improvements + + +

Bump .NET SDK to version `7.0.100-rc.1`

+
+ +
+
    +
  • Update ThirdPartyNotices.txt for 7.3.0-preview.8 (Internal 22553)
  • +
  • Update cgmanifest.json for 7.3.0-preview.8 (Internal 22551)
  • +
  • Re-enable building with Ready-to-Run (#18107)
  • +
  • Make sure Security.types.ps1xml gets signed in release build (#17930)
  • +
  • Update DotnetRuntimeMetadata.json for .NET 7 RC1 build (#18106)
  • +
  • Add XML reference documents to NuPkg files for SDK (#18017)
  • +
  • Make Register MU timeout (#17995)
  • +
  • Bump Microsoft.NET.Test.Sdk from 17.2.0 to 17.3.0 (#17924)
  • +
  • Update list of PS team members in release tools (#17928)
  • +
  • Update to use version 2.21.0 of Application Insights (#17927)
  • +
  • Complete ongoing Write-Progress in test (#17922)
  • +
+
+ +[7.3.0-preview.8]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.7...v7.3.0-preview.8 + +## [7.3.0-preview.7] - 2022-08-09 + +### Breaking Changes + +- Move the type data definition of `System.Security.AccessControl.ObjectSecurity` to the `Microsoft.PowerShell.Security` module (#16355) (Thanks @iSazonov!) + +### Engine Updates and Fixes + +- Enable searching for assemblies in `GAC_Arm64` on Windows (#17816) +- Fix parser exception in using statements with empty aliases (#16745) (Thanks @MartinGC94!) +- Do not always collapse space between parameter and value for native arguments. (#17708) +- Remove `PSNativePSPathResolution` experimental feature (#17670) + +### General Cmdlet Updates and Fixes + +- Fix for deserializing imported ordered dictionary (#15545) (Thanks @davidBar-On!) +- Make generated implicit remoting modules backwards compatible with PowerShell 5.1 (#17227) (Thanks @Tadas!) +- Re-enable IDE0031: Use Null propagation (#17811) (Thanks @fflaten!) +- Allow commands to still be executed even if the current working directory no longer exists (#17579) +- Stop referencing `Microsoft.PowerShell.Security` when the core snapin is used (#17771) +- Add support for HTTPS with `Set-AuthenticodeSignature -TimeStampServer` (#16134) (Thanks @Ryan-Hutchison-USAF!) +- Add type accelerator `ordered` for `OrderedDictionary` (#17804) (Thanks @fflaten!) +- Fix the definition of the `PDH_COUNTER_INFO` struct (#17779) +- Adding Virtualization Based Security feature names to Get-ComputerInfo (#16415) (Thanks @mattifestation!) +- Fix `FileSystemProvider` to work with volume and pipe paths (#15873) +- Remove pre-parse for array-based JSON (#15684) (Thanks @strawgate!) +- Improve type inference for `$_` (#17716) (Thanks @MartinGC94!) +- Prevent braces from being removed when completing variables (#17751) (Thanks @MartinGC94!) +- Fix type inference for `ICollection` (#17752) (Thanks @MartinGC94!) +- Fix `Test-Json` not handling non-object types at root (#17741) (Thanks @dkaszews!) +- Change `Get-ChildItem` to treat trailing slash in path as indicating a directory when used with `-Recurse` (#17704) +- Add `find.exe` to legacy argument binding behavior for Windows (#17715) +- Add completion for index expressions for dictionaries (#17619) (Thanks @MartinGC94!) +- Fix enum-ranges for `ValidateRange` in proxy commands (#17572) (Thanks @fflaten!) +- Fix type completion for attribute tokens (#17484) (Thanks @MartinGC94!) +- Add `-noprofileloadtime` switch to `pwsh` (#17535) (Thanks @rkeithhill!) +- Fix legacy `ErrorView` types to use `$host.PrivateData` colors (#17705) +- Improve dynamic parameter tab completion (#17661) (Thanks @MartinGC94!) +- Avoid binding positional parameters when completing parameter in front of value (#17693) (Thanks @MartinGC94!) +- Render decimal numbers in a table using current culture (#17650) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@fflaten, @Molkree, @eltociear

+ +
+ +
    +
  • Fix other path constructions using Path.Join (#17825)
  • +
  • Use null propagation (#17787)(#17789)(#17790)(#17791)(#17792)(#17795) (Thanks @fflaten!)
  • +
  • Re-enable compound assignment preference (#17784) (Thanks @Molkree!)
  • +
  • Use null-coalescing assignment (#17719)(#17720)(#17721)(#17722)(#17723)(#17724)(#17725)(#17726)(#17727)(#17728)(#17729) (Thanks @Molkree!)
  • +
  • Disable the warning IDE0031 to take .NET 7 Preview 7 (#17770)
  • +
  • Fix typo in ModuleCmdletBase.cs (#17714) (Thanks @eltociear!)
  • +
+ +
+ +### Tests + +- Re-enable tests because the corresponding dotnet issues were fixed (#17839) +- Add test for `LanguageMode` using remoting (#17803) (Thanks @fflaten!) +- Fix test perf by stopping ongoing `write-progress` (#17749) (Thanks @fflaten!) +- Re-enable the test `TestLoadNativeInMemoryAssembly` (#17738) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@varunsh-coder, @dkaszews, @Molkree, @ChuckieChen945

+ +
+ +
    +
  • Update release pipeline to use Approvals and automate some manual tasks (#17837)
  • +
  • Add GitHub token permissions for workflows (#17781) (Thanks @varunsh-coder!)
  • +
  • Bump actions/github-script from 3 to 6 (#17842)
  • +
  • Bump cirrus-actions/rebase from 1.6 to 1.7 (#17843)
  • +
  • Remove unneeded verbose message in build (#17840)
  • +
  • Detect default runtime using dotnet --info in build.psm1 (#17818) (Thanks @dkaszews!)
  • +
  • Bump actions/checkout from 2 to 3 (#17828)
  • +
  • Bump actions/download-artifact from 2 to 3 (#17829)
  • +
  • Bump github/codeql-action from 1 to 2 (#17830)
  • +
  • Bump peter-evans/create-pull-request from 3 to 4 (#17831)
  • +
  • Bump actions/upload-artifact from 2 to 3 (#17832)
  • +
  • Enable Dependabot for GitHub Actions (#17775) (Thanks @Molkree!)
  • +
  • Update .NET SDK version from 7.0.100-preview.6.22352.1 to 7.0.100-preview.7.22377.5 (#17776)
  • +
  • Fix a bug in install-powershell.ps1 (#17794) (Thanks @ChuckieChen945!)
  • +
  • Bump xunit from 2.4.1 to 2.4.2 (#17817)
  • +
  • Update how to update homebrew (#17798)
  • +
  • Don't run link check on forks (#17797)
  • +
  • Update dotnetmetadata.json to start consuming .NET 7 preview 7 builds (#17736)
  • +
  • Bump PackageManagement from 1.4.7 to 1.4.8.1 (#17709)
  • +
  • Exclude ARM images from running in CI (#17713)
  • +
+ +
+ +### Documentation and Help Content + +- Update the comment about why R2R is disabled (#17850) +- Update changelog and `.spelling` for `7.3.0-preview.6` release (#17835) +- Updated `ADOPTERS.md` for Power BI (#17766) +- Update README.md with the current Fedora version (#15717) (Thanks @ananya26-vishnoi!) +- Update `README` and `metadata.json` for next release (#17676) (Thanks @SeeminglyScience!) + +[7.3.0-preview.7]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.6...v7.3.0-preview.7 + +## [7.3.0-preview.6] - 2022-07-18 + +### General Cmdlet Updates and Fixes + +- Fix `Export-PSSession` to not throw error when a rooted path is specified for `-OutputModule` (#17671) +- Change `ConvertFrom-Json -AsHashtable` to use ordered hashtable (#17405) +- Remove potential ANSI escape sequences in strings before using in `Out-GridView` (#17664) +- Add the `-Milliseconds` parameter to `New-TimeSpan` (#17621) (Thanks @NoMoreFood!) +- Update `Set-AuthenticodeSignature` to use `SHA256` as the default (#17560) (Thanks @jborean93!) +- Fix tab completion regression when completing `ValidateSet` values (#17628) (Thanks @MartinGC94!) +- Show optional parameters as such when displaying method definition and overloads (#13799) (Thanks @eugenesmlv!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@sethvs, @MartinGC94, @eltociear

+ +
+ +
    +
  • Fix comment in InternalCommands.cs (#17669) (Thanks @sethvs!)
  • +
  • Use discards for unused variables (#17620) (Thanks @MartinGC94!)
  • +
  • Fix typo in CommonCommandParameters.cs (#17524) (Thanks @eltociear!)
  • +
+ +
+ +### Tests + +- Fix SDK tests for release build (#17678) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@tamasvajk

+ +
+ +
    +
  • Create test artifacts for Windows ARM64 (#17675)
  • +
  • Update to the latest NOTICES file (#17607)
  • +
  • Update .NET SDK version from 7.0.100-preview.5.22307.18 to 7.0.100-preview.6.22352.1 (#17634)
  • +
  • Set the compound assignment preference to false (#17632)
  • +
  • Update DotnetMetadata.json to start consuming .NET 7 Preview 6 builds (#17630)
  • +
  • Install .NET 3.1 as it is required by the vPack task (#17600)
  • +
  • Update to use PSReadLine v2.2.6 (#17595)
  • +
  • Fix build.psm1 to not specify both version and quality for dotnet-install (#17589) (Thanks @tamasvajk!)
  • +
  • Bump Newtonsoft.Json in /test/perf/dotnet-tools/Reporting (#17592)
  • +
  • Bump Newtonsoft.Json in /test/perf/dotnet-tools/ResultsComparer (#17566)
  • +
  • Disable RPM SBOM test. (#17532)
  • +
+ +
+ +### Documentation and Help Content + +- Remove `katacoda.com` from doc as it now returns 404 (#17625) +- Update change log for `v7.2.5` and `v7.3.0-preview.5` (#17565) +- Update `README.md` and `metadata.json` for upcoming releases (#17526) + +[7.3.0-preview.6]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.5...v7.3.0-preview.6 + +## [7.3.0-preview.5] - 2022-06-21 + +### Engine Updates and Fixes + +- Improve type inference and completions (#16963) (Thanks @MartinGC94!) +- Make `Out-String` and `Out-File` keep string input unchanged (#17455) +- Make `AnsiRegex` able to capture Hyperlink ANSI sequences (#17442) +- Add the `-ConfigurationFile` command line parameter to `pwsh` to support local session configuration (#17447) +- Fix native library loading for `osx-arm64` (#17365) (Thanks @awakecoding!) +- Fix formatting to act appropriately when the style of table header or list label is empty string (#17463) + +### General Cmdlet Updates and Fixes + +- Fix various completion issues inside the `param` block (#17489) (Thanks @MartinGC94!) +- Add Amended switch to `Get-CimClass` cmdlet (#17477) (Thanks @iSazonov!) +- Improve completion on operators (#17486) (Thanks @MartinGC94!) +- Improve array element completion for command arguments (#17078) (Thanks @matt9ucci!) +- Use AST extent for `PSScriptRoot` path completion (#17376) +- Add type inference support for generic methods with type parameters (#16951) (Thanks @MartinGC94!) +- Write out OSC indicator only if the `stdout` is not redirected (#17419) +- Remove the assert and use a relatively larger capacity to cover possible increase of .NET reference assemblies (#17423) +- Increase reference assembly count to 161 (#17420) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@Yulv-git, @eltociear

+ +
+ +
    +
  • Fix some typos in source code (#17481) (Thanks @Yulv-git!)
  • +
  • Fix typo in `AsyncResult.cs` (#17396) (Thanks @eltociear!)
  • +
+ +
+ +### Tools + +- Update script to pin to .NET 7 preview 5 version (#17448) +- Start-PSPester: argument completer for `-Path` (#17334) (Thanks @powercode!) +- Add reminder workflows (#17387) +- Move to configuring the fabric bot via JSON (#17411) +- Update Documentation Issue Template URL (#17410) (Thanks @michaeltlombardi!) +- Update script to automatically take new preview pre-release builds (#17375) + +### Tests + +- Make Assembly Load Native test work on a FX Dependent Linux Install (#17380) +- Update `Get-Error` test to not depend on DNS APIs (#17471) + +### Build and Packaging Improvements + +
+ +
    +
  • Update .NET SDK version from 7.0.100-preview.4.22252.9 to 7.0.100-preview.5.22307.18 (#17402)
  • +
  • Downgrade the Microsoft.CodeAnalysis.NetAnalyzers package to 7.0.0-preview1.22217.1 (#17515)
  • +
  • Rename mariner package to cm (#17505)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#17476)
  • +
  • Bump NJsonSchema from 10.7.1 to 10.7.2 (#17475)
  • +
  • Publish preview versions of mariner to preview repo (#17451)
  • +
  • Update to the latest NOTICES file (#17421)
  • +
  • Do not publish package for Mariner 1.0 (#17415)
  • +
  • Add AppX capabilities in MSIX manifest so that PS7 can call the AppX APIs (#17416)
  • +
  • Update to the latest NOTICES file (#17401)
  • +
  • Fix mariner mappings (#17413)
  • +
  • Update the cgmanifest (#17393)
  • +
  • Bump `NJsonSchema` from `10.7.0` to `10.7.1` (#17381)
  • +
+ +
+ +### Documentation and Help Content + +- Update to the latest NOTICES file (#17493) (Thanks @github-actions[bot]!) +- Update the cgmanifest (#17478) (Thanks @github-actions[bot]!) +- Correct spelling in Comments and tests (#17480) (Thanks @Yulv-git!) +- Fix spelling errors introduced in changelog (#17414) +- Update change log for v7.3.0-preview.4 release (#17412) +- Update readme and metadata for 7.3.0-preview.4 release (#17378) + +[7.3.0-preview.5]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.4...v7.3.0-preview.5 + +## [7.3.0-preview.4] - 2022-05-23 + +### Engine Updates and Fixes + + + +### General Cmdlet Updates and Fixes + + + +### Code Cleanup + + + +### Documentation and Help Content + + + +### Tests + + + +### Build and Packaging Improvements + + + +[7.3.0-preview.4]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.3...v7.3.0-preview.4 + +## [7.3.0-preview.3] - 2022-03-21 + +### Engine Updates and Fixes + +- Fix the parsing code for .NET method generic arguments (#16937) +- Allow the `PSGetMemberBinder` to get value of `ByRef` property (#16956) +- Allow a collection that contains `Automation.Null` elements to be piped to pipeline (#16957) + +### General Cmdlet Updates and Fixes + +- Add the module `CompatPowerShellGet` to the allow-list of telemetry modules (#16935) +- Fix `Enter-PSHostProcess` and `Get-PSHostProcessInfo` cmdlets by handling processes that have exited (#16946) +- Improve Hashtable completion in multiple scenarios (#16498) (Thanks @MartinGC94!) + +### Code Cleanup + +- Fix a typo in `CommandHelpProvider.cs` (#16949) (Thanks @eltociear!) + +### Tests + +- Update a few tests to make them more stable in CI (#16944) +- Roll back Windows images used in testing to Windows Server 2019 (#16958) + +### Build and Packaging Improvements + +
+ + +

Update .NET SDK to 7.0.0-preview.2

+
+ +
    +
  • Update .NET to 7.0.0-preview.2 build (#16930)
  • +
  • Update AzureFileCopy task and fix the syntax for specifying pool (#17013)
  • +
+ +
+ +[7.3.0-preview.3]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.2...v7.3.0-preview.3 + +## [7.3.0-preview.2] - 2022-02-24 + +### Engine Updates and Fixes + +- Fix the `clean` block for generated proxy function (#16827) +- Add support to allow invoking method with generic type arguments (#12412 and #16822) (Thanks @vexx32!) +- Report error when PowerShell built-in modules are missing (#16628) + +### General Cmdlet Updates and Fixes + +- Prevent command completion if the word to complete is a single dash (#16781) (Thanks @ayousuf23!) +- Use `FindFirstFileW` instead of `FindFirstFileExW` to correctly handle Unicode file names on FAT32 (#16840) (Thanks @iSazonov!) +- Add completion for loop labels after Break/Continue (#16438) (Thanks @MartinGC94!) +- Support OpenSSH options for `PSRP` over SSH commands (#12802) (Thanks @BrannenGH!) +- Adds a `.ResolvedTarget` Property to `File-System` Items to Reflect a Symlink's Target as `FileSystemInfo` (#16490) (Thanks @hammy3502!) +- Use `NotifyEndApplication` to re-enable VT mode (#16612) +- Add new parameter to `Start-Sleep`: `[-Duration] ` (#16185) (Thanks @IISResetMe!) +- Add lock and null check to remoting internals (#16542) (#16683) (Thanks @SergeyZalyadeev!) +- Make `Measure-Object` ignore missing properties unless running in strict mode (#16589) (Thanks @KiwiThePoodle!) +- Add `-StrictMode` to `Invoke-Command` to allow specifying strict mode when invoking command locally (#16545) (Thanks @Thomas-Yu!) +- Fix `$PSNativeCommandArgPassing` = `Windows` to handle empty args correctly (#16639) +- Reduce the amount of startup banner text (#16516) (Thanks @rkeithhill!) +- Add `exec` cmdlet for bash compatibility (#16462) +- Add AMSI method invocation logging as experimental feature (#16496) +- Fix web cmdlets so that an empty `Get` does not include a `content-length` header (#16587) +- Update `HelpInfoUri` for 7.3 release (#16646) +- Fix parsing `SemanticVersion` build label from version string (#16608) +- Fix `ForEach-Object -Parallel` when passing in script block variable (#16564) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@eltociear, @iSazonov, @xtqqczze

+ +
+ +
    +
  • Fix typo in PowerShellExecutionHelper.cs (#16776) (Thanks @eltociear!)
  • +
  • Use more efficient platform detection API (#16760) (Thanks @iSazonov!)
  • +
  • Seal ClientRemotePowerShell (#15802) (Thanks @xtqqczze!)
  • +
  • Fix the DSC overview URL in a markdown file and some small cleanup changes (#16629)
  • +
+ +
+ +### Tools + +- Fix automation to update experimental JSON files in GitHub action (#16837) + +### Tests + +- Update `markdownlint` to the latest version (#16825) +- Bump the package `path-parse` from `1.0.6` to `1.0.7` (#16820) +- Remove assert that is incorrect and affecting our tests (#16588) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@dahlia

+ +
+ +
    +
  • Update NuGet Testing to not re-install dotnet, +when not needed and dynamically determine the DOTNET_ROOT (Internal 19268, 19269, 19272, 19273, and 19274)
  • +
  • Remove SkipExperimentalFeatureGeneration when building alpine (Internal 19248)
  • +
  • Revert .NET 7 changes, Update to the latest .NET 6 and Update WXS file due to blocking issue in .NET 7 Preview 1
  • +
  • Install and Find AzCopy
  • +
  • Use Start-PSBootStrap for installing .NET during nuget packaging
  • +
  • Fix pool syntax for deployments (Internal 19189)
  • +
  • Bump NJsonSchema from 10.5.2 to 10.6.9 (#16888)
  • +
  • Update projects and scripts to use .NET 7 preview 1 pre-release builds (#16856)
  • +
  • Add warning messages when package precheck fails (#16867)
  • +
  • Refactor Global Tool packaging to include SBOM generation (#16860)
  • +
  • Update to use windows-latest as the build agent image (#16831)
  • +
  • Ensure alpine and arm SKUs have powershell.config.json file with experimental features enabled (#16823)
  • +
  • Update experimental feature json files (#16838) (Thanks @github-actions[bot]!)
  • +
  • Remove WiX install (#16834)
  • +
  • Add experimental json update automation (#16833)
  • +
  • Update .NET SDK to 6.0.101 and fix Microsoft.PowerShell.GlobalTool.Shim.csproj (#16821)
  • +
  • Add SBOM manifest to nuget packages (#16711)
  • +
  • Improve logic for updating .NET in CI (#16808)
  • +
  • Add Linux package dependencies for packaging (#16807)
  • +
  • Switch to our custom images for build and release (#16801)
  • +
  • Remove all references to cmake for the builds in this repo (#16578)
  • +
  • Fix build for new InvokeCommand attributes (#16800)
  • +
  • Let macOS installer run without Rosetta on Apple Silicon (#16742) (Thanks @dahlia!)
  • +
  • Update the expect .NET SDK quality to GA for installing dotnet (#16784)
  • +
  • Change nuget release yaml to use UseDotNet task (#16701)
  • +
  • Bump Microsoft.ApplicationInsights from 2.19.0 to 2.20.0 (#16642)
  • +
  • Register NuGet source when generating CGManifest (#16570)
  • +
  • Update Images used for release (#16580)
  • +
  • Update SBOM generation (#16641)
  • +
  • Bring changes from 7.3.0-preview.1 (#16640)
  • +
  • Update the vmImage and PowerShell root directory for macOS builds (#16611)
  • +
  • Update macOS build image and root folder for build (#16609)
  • +
  • Disabled Yarn cache in markdown.yml (#16599)
  • +
  • Update cgmanifest (#16600)
  • +
  • Fix broken links in markdown (#16598)
  • +
+ +
+ +### Documentation and Help Content + +- Add newly joined members to their respective Working Groups (#16849) +- Update Engine Working Group members (#16780) +- Replace the broken link about pull request (#16771) +- Update change log to remove a broken URL (#16735) +- Updated `README.md` and `metadata.json` for `v7.3.0-preview.1` release (#16627) +- Updating changelog for `7.2.1` (#16616) +- Updated `README.md` and `metadata.json` for `7.2.1` release (#16586) + +[7.3.0-preview.2]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.1...v7.3.0-preview.2 + +## [7.3.0-preview.1] - 2021-12-16 + +### Breaking Changes + +- Add `clean` block to script block as a peer to `begin`, `process`, and `end` to allow easy resource cleanup (#15177) +- Change default for `$PSStyle.OutputRendering` to `Ansi` (Internal 18449) + +### Engine Updates and Fixes + +- Remove duplicate remote server mediator code (#16027) +- Fix `PSVersion` parameter version checks and error messages for PowerShell 7 remoting (#16228) +- Use the same temporary home directory when `HOME` env variable is not set (#16263) +- Fix parser to generate error when array has more than 32 dimensions (#16276) + +### Performance + +- Avoid validation for built-in file extension and color VT sequences (#16320) (Thanks @iSazonov!) + +### General Cmdlet Updates and Fixes + +- Update `README.md` and `metadata.json` for next preview release (#16107) +- Use `PlainText` when writing to a host that doesn't support VT (#16092) +- Remove support for `AppExeCLinks` to retrieve target (#16044) +- Move `GetOuputString()` and `GetFormatStyleString()` to `PSHostUserInterface` as public API (#16075) +- Fix `ConvertTo-SecureString` with key regression due to .NET breaking change (#16068) +- Fix regression in `Move-Item` to only fallback to `copy and delete` in specific cases (#16029) +- Set `$?` correctly for command expression with redirections (#16046) +- Use `CurrentCulture` when handling conversions to `DateTime` in `Add-History` (#16005) (Thanks @vexx32!) +- Fix link header parsing to handle unquoted `rel` types (#15973) (Thanks @StevenLiekens!) +- Fix a casting error when using `$PSNativeCommandUsesErrorActionPreference` (#15993) +- Format-Wide: Fix `NullReferenceException` (#15990) (Thanks @DarylGraves!) +- Make the native command error handling optionally honor `ErrorActionPreference` (#15897) +- Remove declaration of experimental features in Utility module manifest as they are stable (#16460) +- Fix race condition between `DisconnectAsync` and `Dispose` (#16536) (Thanks @i3arnon!) +- Fix the `Max_PATH` condition check to handle long path correctly (#16487) (Thanks @Shriram0908!) +- Update `HelpInfoUri` for 7.2 release (#16456) +- Fix tab completion within the script block specified for the `ValidateScriptAttribute`. (#14550) (Thanks @MartinGC94!) +- Update `README.md` to specify gathered telemetry (#16379) +- Fix typo for "privacy" in MSI installer (#16407) +- Remove unneeded call to `File.ResolveLinkTarget` from `IsWindowsApplication` (#16371) (Thanks @iSazonov!) +- Add `-HttpVersion` parameter to web cmdlets (#15853) (Thanks @hayhay27!) +- Add support to web cmdlets for open-ended input tags (#16193) (Thanks @farmerau!) +- Add more tests to `Tee-Object -Encoding` (#14539) (Thanks @rpolley!) +- Don't throw exception when trying to resolve a possible link path (#16310) +- Fix `ConvertTo-Json -Depth` to allow 100 at maximum (#16197) (Thanks @KevRitchie!) +- Fix for SSH remoting when banner is enabled on SSHD endpoint (#16205) +- Disallow all COM for AppLocker system lock down (#16268) +- Configure `ApplicationInsights` to not send cloud role name (#16246) +- Disallow `Add-Type` in NoLanguage mode on a locked down machine (#16245) +- Specify the executable path as `TargetObect` for non-zero exit code `ErrorRecord` (#16108) (Thanks @rkeithhill!) +- Don't allow `Move-Item` with FileSystemProvider to move a directory into itself (#16198) +- Make property names for the color VT sequences consistent with documentations (#16212) +- Fix `PipelineVariable` to set variable in the right scope (#16199) +- Invoke-Command: improve handling of variables with $using: expression (#16113) (Thanks @dwtaber!) +- Change `Target` from a `CodeProperty` to be an `AliasProperty` that points to `FileSystemInfo.LinkTarget` (#16165) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @eltociear, @iSazonov

+ +
+ +
    +
  • Improve CommandInvocationIntrinsics API documentation and style (#14369)
  • +
  • Use bool?.GetValueOrDefault() in FormatWideCommand (#15988) (Thanks @xtqqczze!)
  • +
  • Remove 4 assertions which cause debug build test runs to fail (#15963)
  • +
  • Fix typo in `Job.cs` (#16454) (Thanks @eltociear!)
  • +
  • Remove unnecessary call to `ToArray` (#16307) (Thanks @iSazonov!)
  • +
  • Remove the unused `FollowSymLink` function (#16231)
  • +
  • Fix typo in `TypeTable.cs` (#16220) (Thanks @eltociear!)
  • +
  • Fixes #16176 - replace snippet tag with code tag in comments (#16177)
  • +
+ +
+ +### Tools + +- Fix typo in build.psm1 (#16038) (Thanks @eltociear!) +- Add `.stylecop` to `filetypexml` and format it (#16025) +- Enable sending Teams notification when workflow fails (#15982) +- Use `Convert-Path` for unknown drive in `Build.psm1` (#16416) (Thanks @matt9ucci!) + +### Tests + +- Add benchmark to test compiler performance (#16083) +- Enable two previously disabled `Get-Process` tests (#15845) (Thanks @iSazonov!) +- Set clean state before testing `UseMU` in the MSI (#16543) +- Fix global tool and SDK tests in release pipeline (#16342) +- Remove the outdated test (#16269) +- Removed old not-used-anymore docker-based tests for PS release packages (#16224) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@github-actions[bot], @kondratyev-nv

+ +
+ +
    +
  • fix issue with hash file getting created before we have finished get-childitem (#16170)
  • +
  • Add sha256 hashes to release (#16147)
  • +
  • Change path for Component Governance for build to the path we actually use to build (#16137)
  • +
  • Update Microsoft.CodeAnalysis.CSharp version (#16138)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#16070)
  • +
  • Update .NET to 6.0.100-rc.1.21458.32 (#16066)
  • +
  • Update minimum required OS version for macOS (#16088)
  • +
  • Set locale correctly on Linux CI (#16073)
  • +
  • Ensure locale is set correctly on Ubuntu 20.04 in CI (#16067)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#16045)
  • +
  • Update .NET SDK version from `6.0.100-rc.1.21430.44` to `6.0.100-rc.1.21455.2` (#16041) (Thanks @github-actions[bot]!)
  • +
  • Fix the GitHub Action for updating .NET daily builds (#16042)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 4.0.0-3.final to 4.0.0-4.21430.4 (#16036)
  • +
  • Bump .NET to `6.0.100-rc.1.21430.44` (#16028)
  • +
  • Move from PkgES hosted agents to 1ES hosted agents (#16023)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#16021)
  • +
  • Update Ubuntu images to use Ubuntu 20.04 (#15906)
  • +
  • Fix the mac build by updating the pool image name (#16010)
  • +
  • Use Alpine 3.12 for building PowerShell for alpine (#16008)
  • +
  • Update .NET SDK version from `6.0.100-preview.6.21355.2` to `6.0.100-rc.1.21426.1` (#15648) (Thanks @github-actions[bot]!)
  • +
  • Ignore error from Find-Package (#15999)
  • +
  • Find packages separately for each source in UpdateDotnetRuntime.ps1 script (#15998)
  • +
  • Update metadata to start using .NET 6 RC1 builds (#15981)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#15985)
  • +
  • Merge the v7.2.0-preview.9 release branch back to GitHub master (#15983)
  • +
  • Publish global tool package for stable releases (#15961)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers to newer version (#15962)
  • +
  • Disabled Yarn cache in markdown.yml (#16599)
  • +
  • Update cgmanifest (#16600)
  • +
  • Fix broken links in markdown (#16598)
  • +
  • Add explicit job name for approval tasks in Snap stage (#16579)
  • +
  • Bring back pwsh.exe for framework dependent packages to support Start-Job (#16535)
  • +
  • Fix NuGet package generation in release build (#16509)
  • +
  • Add `Microsoft.PowerShell.Commands.SetStrictModeCommand.ArgumentToPSVersionTransformationAttribute` to list of patterns to remove for generated ref assembly (#16489)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from `4.0.0-6.final` to `4.0.1` (#16423)
  • +
  • use different containers for different branches (#16434)
  • +
  • Add import so we can use common GitHub workflow function. (#16433)
  • +
  • Remove pre-release .NET 6 build sources (#16418)
  • +
  • Update release instructions with link to new build (#16419)
  • +
  • Bump Microsoft.ApplicationInsights from 2.18.0 to 2.19.0 (#16413)
  • +
  • Update metadata.json to make 7.2.0 the latest LTS (#16417)
  • +
  • Make static CI a matrix (#16397)
  • +
  • Update metadata.json in preparation on 7.3.0-preview.1 release (#16406)
  • +
  • Update cgmanifest (#16405)
  • +
  • Add diagnostics used to take corrective action when releasing `buildInfoJson` (#16404)
  • +
  • `vPack` release should use `buildInfoJson` new to 7.2 (#16402)
  • +
  • Update the usage of metadata.json for getting LTS information (#16381)
  • +
  • Add checkout to build json stage to get `ci.psm1` (#16399)
  • +
  • Update CgManifest.json for 6.0.0 .NET packages (#16398)
  • +
  • Add current folder to the beginning of the module import (#16353)
  • +
  • Increment RC MSI build number by 100 (#16354)
  • +
  • Bump XunitXml.TestLogger from 3.0.66 to 3.0.70 (#16356)
  • +
  • Move PR Quantifier config to subfolder (#16352)
  • +
  • Release build info json when it is preview (#16335)
  • +
  • Add an approval for releasing build-info json (#16351)
  • +
  • Generate manifest with latest public version of the packages (#16337)
  • +
  • Update to the latest notices file (#16339) (Thanks @github-actions[bot]!)
  • +
  • Use notice task to generate license assuming cgmanifest contains all components (#16340)
  • +
  • Refactor cgmanifest generator to include all components (#16326)
  • +
  • Fix issues in release build (#16332)
  • +
  • Update feed and analyzer dependency (#16327)
  • +
  • Bump Microsoft.NET.Test.Sdk from 16.11.0 to 17.0.0 (#16312)
  • +
  • Update license and cgmanifest (#16325) (Thanks @github-actions[bot]!)
  • +
  • Fix condition in cgmanifest logic (#16324)
  • +
  • Add GitHub Workflow to keep notices up to date (#16284)
  • +
  • Update to latest .NET 6 GA build 6.0.100-rtm.21527.11 (#16309)
  • +
  • Create compliance build (#16286)
  • +
  • Move mapping file into product repo and add Debian 11 (#16316)
  • +
  • Add a major-minor build info JSON file (#16301)
  • +
  • Clean up crossgen related build scripts also generate native symbols for R2R images (#16297)
  • +
  • Fix Windows build ZIP packaging (#16299) (Thanks @kondratyev-nv!)
  • +
  • Revert "Update to use .NET 6 GA build (#16296)" (#16308)
  • +
  • Add wget as a dependency for Bootstrap script (#16303) (Thanks @kondratyev-nv!)
  • +
  • Fix issues reported by code signing verification tool (#16291)
  • +
  • Update to use .NET 6 GA build (#16296)
  • +
  • Revert "add GH workflow to keep the cgmanifest up to date." (#16294)
  • +
  • Update ChangeLog for 7.2.0-rc.1 and also fix RPM packaging (#16290)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#16271)
  • +
  • add GH workflow to keep the cgmanifest up to date.
  • +
  • Update ThirdPartyNotices.txt (#16283)
  • +
  • Update `testartifacts.yml` to use ubuntu-latest image (#16279)
  • +
  • Update version of Microsoft.PowerShell.Native and Microsoft.PowerShell.MarkdownRender packages (#16277)
  • +
  • Add script to generate cgmanifest.json (#16278)
  • +
  • Add cgmanifest.json for generating correct third party notice file (#16266)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers from `6.0.0-rtm.21504.2` to `6.0.0-rtm.21516.1` (#16264)
  • +
  • Only upload stable buildinfo for stable releases (#16251)
  • +
  • Make RPM license recognized (#16189)
  • +
  • Don't upload dep or tar.gz for RPM because there are none. (#16230)
  • +
  • Add condition to generate release files in local dev build only (#16259)
  • +
  • Update .NET 6 to version 6.0.100-rc.2.21505.57 (#16249)
  • +
  • change order of try-catch-finally and split out arm runs (#16252)
  • +
  • Ensure psoptions.json and manifest.spdx.json files always exist in packages (#16258)
  • +
  • Update to vPack task version to 12 (#16250)
  • +
  • Remove unneeded `NuGetConfigFile` resource string (#16232)
  • +
  • Add Software Bill of Materials to the main packages (#16202)
  • +
  • Sign third party exes (#16229)
  • +
  • Upgrade set-value package for markdown test (#16196)
  • +
  • Use Ubuntu 20.04 for SSH remoting test (#16225)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#16194)
  • +
  • Bump `Microsoft.CodeAnalysis.NetAnalyzers` from `6.0.0-rc2.21458.5` to `6.0.0-rtm.21480.8` (#16183)
  • +
  • Move vPack build to 1ES Pool (#16169)
  • +
  • Fix Microsoft update spelling issue. (#16178)
  • +
+ +
+ +### Documentation and Help Content + +- Update Windows PowerShell issues link (#16105) (Thanks @andschwa!) +- Remove Joey from Committee and WG membership (#16119) +- Update more docs for `net6.0` TFM (#16102) (Thanks @xtqqczze!) +- Change `snippet` tag to `code` tag in XML comments (#16106) +- Update build documentation to reflect .NET 6 (#15751) (Thanks @Kellen-Stuart!) +- Update `README.md` about the change logs (#16471) (Thanks @powershellpr0mpt!) +- Update change log for 7.2.0 (#16401) +- Update `metadata.json` and `README.md` for 7.2.0 release (#16395) +- Update `README.md` and `metadata.json` files for `v7.2.0-rc.1` release (#16285) +- Update the change logs for `v7.0.8` and `v7.1.5` releases (#16248) + +[7.3.0-preview.1]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.10...v7.3.0-preview.1 diff --git a/CHANGELOG/README.md b/CHANGELOG/README.md index 83efcb0fed5..60ae3af38c4 100644 --- a/CHANGELOG/README.md +++ b/CHANGELOG/README.md @@ -1,6 +1,7 @@ # Changelogs * [Current preview changelog](preview.md) +* [7.2 changelog](7.2.md) * [7.1 changelog](7.1.md) * [7.0 changelog](7.0.md) * [6.2 changelog](6.2.md) diff --git a/CHANGELOG/preview.md b/CHANGELOG/preview.md index a722a67c6e9..035db721385 100644 --- a/CHANGELOG/preview.md +++ b/CHANGELOG/preview.md @@ -1,1034 +1 @@ # Current preview release - -## [7.2.0-preview.10] - 2021-09-28 - -### Engine Updates and Fixes - -- Remove duplicate remote server mediator code (#16027) - -### General Cmdlet Updates and Fixes - -- Use `PlainText` when writing to a host that doesn't support VT (#16092) -- Remove support for `AppExecLinks` to retrieve target (#16044) -- Move `GetOuputString()` and `GetFormatStyleString()` to `PSHostUserInterface` as public API (#16075) -- Add `isOutputRedirected` parameter to `GetFormatStyleString()` method (#14397) -- Fix `ConvertTo-SecureString` with key regression due to .NET breaking change (#16068) -- Fix regression in `Move-Item` to only fallback to `CopyAndDelete` in specific cases (#16029) -- Set `$?` correctly for command expression with redirection (#16046) -- Use `CurrentCulture` when handling conversions to `DateTime` in `Add-History` (#16005) (Thanks @vexx32!) -- Fix `NullReferenceException` in `Format-Wide` (#15990) (Thanks @DarylGraves!) - -### Code Cleanup - -
- - - -

We thank the following contributors!

-

@xtqqczze!

- -
- -
    -
  • Improve CommandInvocationIntrinsics API documentation and style (#14369)
  • -
  • Use bool?.GetValueOrDefault() in FormatWideCommand (#15988) (Thanks @xtqqczze!)
  • -
- -
- -### Tools - -- Fix typo in build.psm1 (#16038) (Thanks @eltociear!) -- Add `.stylecop` to `filetypexml` and format it (#16025) -- Enable sending Teams notification when workflow fails (#15982) - -### Tests - -- Enable two previously disabled `Get-Process` tests (#15845) (Thanks @iSazonov!) - -### Build and Packaging Improvements - -
- - -Details - - -
    -
  • Add SHA256 hashes to release (#16147)
  • -
  • Update Microsoft.CodeAnalysis.CSharp version (#16138)
  • -
  • Change path for Component Governance for build to the path we actually use to build (#16137)
  • -
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#16070) (#16045) (#16036) (#16021) (#15985)
  • -
  • Update .NET to 6.0.100-rc.1.21458.32 (#16066)
  • -
  • Update minimum required OS version for macOS (#16088)
  • -
  • Ensure locale is set correctly on Ubuntu 20.04 in CI (#16067) (#16073)
  • -
  • Update .NET SDK version from 6.0.100-preview.6.21355.2 to 6.0.100-rc.1.21455.2 (#16041) (#16028) (#15648)
  • -
  • Fix the GitHub Action for updating .NET daily builds (#16042)
  • -
  • Move from PkgES hosted agents to 1ES hosted agents (#16023)
  • -
  • Update Ubuntu images to use Ubuntu 20.04 (#15906)
  • -
  • Fix the macOS build by updating the pool image name (#16010)
  • -
  • Use Alpine 3.12 for building PowerShell for Alpine Linux (#16008)
  • -
  • Ignore error from Find-Package (#15999)
  • -
  • Find packages separately for each source in UpdateDotnetRuntime.ps1 script (#15998)
  • -
  • Update metadata to start using .NET 6 RC1 builds (#15981)
  • -
- -
- -[7.2.0-preview.10]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.9...v7.2.0-preview.10 - -## [7.2.0-preview.9] - 2021-08-23 - -### Breaking Changes - -- Change the default value of `$PSStyle.OutputRendering` to `OutputRendering.Host` and remove `OutputRendering.Automatic` (#15882) -- Fix `CA1052` for public API to make classes static when they only have static methods (#15775) (Thanks @xtqqczze!) -- Update `pwsh.exe -File` to only accept `.ps1` script files on Windows (#15859) - -### Engine Updates and Fixes - -- Update .NET adapter to handle interface static members properly (#15908) -- Catch and handle unauthorized access exception when removing AppLocker test files (#15881) - -### General Cmdlet Updates and Fixes - -- Add `-PassThru` parameter to `Set-Clipboard` (#13713) (Thanks @ThomasNieto!) -- Add `-Encoding` parameter for `Tee-Object` (#12135) (Thanks @Peter-Schneider!) -- Update `ConvertTo-Csv` and `Export-Csv` to handle `IDictionary` objects (#11029) (Thanks @vexx32!) -- Update the parameters `-Exception` and `-ErrorRecord` for `Write-Error` to be position 0 (#13813) (Thanks @ThomasNieto!) -- Don't use `ArgumentList` when creating COM object with `New-Object` as it's not applicable to the COM parameter set (#15915) -- Fix `$PSStyle` list output to correctly show `TableHeader` (#15928) -- Remove the `PSImplicitRemotingBatching` experimental feature (#15863) -- Fix issue with `Get-Process -Module` failing to stop when it's piped to `Select-Object` (#15682) (Thanks @ArmaanMcleod!) -- Make the experimental features `PSUnixFileStat`, `PSCultureInvariantReplaceOperator`, `PSNotApplyErrorActionToStderr`, `PSAnsiRendering`, `PSAnsiProgressFeatureName` stable (#15864) -- Enhance `Remove-Item` to work with OneDrive (#15571) (Thanks @iSazonov!) -- Make global tool entrypoint class static (#15880) -- Update `ServerRemoteHost` version to be same as `PSVersion` (#15809) -- Make the initialization of `HttpKnownHeaderNames` thread safe (#15519) (Thanks @iSazonov!) -- `ConvertTo-Csv`: Quote fields with quotes and newlines when using `-UseQuotes AsNeeded` (#15765) (Thanks @lselden!) -- Forwarding progress stream changes from `Foreach-Object -Parallel` runspaces (#14271) (Thanks @powercode!) -- Add validation to `$PSStyle` to reject printable text when setting a property that only expects ANSI escape sequence (#15825) - -### Code Cleanup - -
- - - -

We thank the following contributors!

-

@xtqqczze

- -
- -
    -
  • Avoid unneeded array allocation in module code (#14329) (Thanks @xtqqczze!)
  • -
  • Enable and fix analysis rules CA1052, CA1067, and IDE0049 (#15840) (Thanks @xtqqczze!)
  • -
  • Avoid unnecessary allocation in formatting code (#15832) (Thanks @xtqqczze!)
  • -
  • Specify the analyzed API surface for all code quality rules (#15778) (Thanks @xtqqczze!)
  • -
- -
- -### Tools - -- Enable `/rebase` to automatically rebase a PR (#15808) -- Update `.editorconfig` to not replace tabs with spaces in `.tsv` files (#15815) (Thanks @SethFalco!) -- Update PowerShell team members in the change log generation script (#15817) - -### Tests - -- Add more tests to validate the current command error handling behaviors (#15919) -- Make `Measure-Object` property test independent of the file system (#15879) -- Add more information when a `syslog` parsing error occurs (#15857) -- Harden logic when looking for `syslog` entries to be sure that we select based on the process id (#15841) - -### Build and Packaging Improvements - -
- - - -

We thank the following contributors!

-

@xtqqczze

- -
- -
    -
  • Disable implicit namespace imports for test projects (#15895)
  • -
  • Update language version to 10 and fix related issues (#15886)
  • -
  • Update CodeQL workflow to use Ubuntu 18.04 (#15868)
  • -
  • Bump the version of various packages (#15944, #15934, #15935, #15891, #15812, #15822) (Thanks @xtqqczze!)
  • -
- -
- -### Documentation and Help Content - -- Update `README` and `metadata files` for release `v7.2.0-preview.8` (#15819) -- Update change logs for 7.0.7 and 7.1.4 (#15921) -- Fix spelling in XML docs (#15939) (Thanks @slowy07!) -- Update PowerShell Committee members (#15837) - -[7.2.0-preview.9]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.8...v7.2.0-preview.9 - -## [7.2.0-preview.8] - 2021-07-22 - -### Engine Updates and Fixes - -- Add a Windows mode to `$PSNativeCommandArgumentPassing` that allows some commands to use legacy argument passing (#15408) -- Use `nameof` to get parameter names when creating `ArgumentNullException` (#15604) (Thanks @gukoff!) -- Test if a command is 'Out-Default' more thoroughly for transcribing scenarios (#15653) -- Add `Microsoft.PowerShell.Crescendo` to telemetry allow list (#15372) - -### General Cmdlet Updates and Fixes - -- Use `$PSStyle.Formatting.FormatAccent` for `Format-List` and `$PSStyle.Formatting.TableHeader` for `Format-Table` output (#14406) -- Highlight using error color the exception `Message` and underline in `PositionMessage` for `Get-Error` (#15786) -- Implement a completion for View parameter of format cmdlets (#14513) (Thanks @iSazonov!) -- Add support to colorize `FileInfo` file names (#14403) -- Don't serialize to JSON ETS properties for `DateTime` and `string` types (#15665) -- Fix `HyperVSocketEndPoint.ServiceId` setter (#15704) (Thanks @xtqqczze!) -- Add `DetailedView` to `$ErrorView` (#15609) - -### Code Cleanup - -
- - - -

We thank the following contributors!

-

@iSazonov, @xtqqczze

- -
- -
    -
  • Remove consolehost.proto file (#15741) (Thanks @iSazonov!)
  • -
  • Implement IDisposable for ConvertToJsonCommand (#15787) (Thanks @xtqqczze!)
  • -
  • Fix IDisposable implementation for CommandPathSearch (#15793) (Thanks @xtqqczze!)
  • -
  • Delete IDE dispose analyzer rules (#15798) (Thanks @xtqqczze!)
  • -
  • Seal private classes (#15725) (Thanks @xtqqczze!)
  • -
  • Enable IDE0029: UseCoalesceExpression (#15770) (Thanks @xtqqczze!)
  • -
  • Enable IDE0070: UseSystemHashCode (#15715) (Thanks @xtqqczze!)
  • -
  • Enable IDE0030: UseCoalesceExpressionForNullable (#14289) (Thanks @xtqqczze!)
  • -
  • Fix CA1846 and CA1845 for using AsSpan instead of Substring (#15738)
  • -
  • Use List<T>.RemoveAll to avoid creating temporary list (#15686) (Thanks @xtqqczze!)
  • -
  • Enable IDE0044: MakeFieldReadonly (#13880) (Thanks @xtqqczze!)
  • -
  • Disable IDE0130 (#15728) (Thanks @xtqqczze!)
  • -
  • Make classes sealed (#15675) (Thanks @xtqqczze!)
  • -
  • Enable CA1043: Use integral or string argument for indexers (#14467) (Thanks @xtqqczze!)
  • -
  • Enable CA1812 (#15674) (Thanks @xtqqczze!)
  • -
  • Replace Single with First when we know the element count is 1 (#15676) (Thanks @xtqqczze!)
  • -
  • Skip analyzers for Microsoft.Management.UI.Internal (#15677) (Thanks @xtqqczze!)
  • -
  • Fix CA2243: Attribute string literals should parse correctly (#15622) (Thanks @xtqqczze!)
  • -
  • Enable CA1401 (#15621) (Thanks @xtqqczze!)
  • -
  • Fix CA1309: Use ordinal StringComparison in Certificate Provider (#14352) (Thanks @xtqqczze!)
  • -
  • Fix CA1839: Use Environment.ProcessPath (#15650) (Thanks @xtqqczze!)
  • -
  • Add new analyzer rules (#15620) (Thanks @xtqqczze!)
  • -
- -
- -### Tools - -- Add `SkipRoslynAnalyzers` parameter to `Start-PSBuild` (#15640) (Thanks @xtqqczze!) -- Create issue template for issues updating PowerShell through Windows update. (#15700) -- Add `DocumentationAnalyzers` to build (#14336) (Thanks @xtqqczze!) -- Convert GitHub issue templates to modern forms (#15645) - -### Tests - -- Add more tests for `ConvertFrom-Json` (#15706) (Thanks @strawgate!) -- Update `glob-parent` and `hosted-git-info` test dependencies (#15643) - -### Build and Packaging Improvements - -
- - -Update .NET to version v6.0.0-preview.6 - - -
    -
  • Add new package name for osx-arm64 (#15813)
  • -
  • Prefer version when available for dotnet-install (#15810)
  • -
  • Make warning about MU being required dynamic (#15776)
  • -
  • Add Start-PSBootstrap before running tests (#15804)
  • -
  • Update to .NET 6 Preview 6 and use crossgen2 (#15763)
  • -
  • Enable ARM64 packaging for macOS (#15768)
  • -
  • Make Microsoft Update opt-out/in check boxes work (#15784)
  • -
  • Add Microsoft Update opt out to MSI install (#15727)
  • -
  • Bump NJsonSchema from 10.4.4 to 10.4.5 (#15769)
  • -
  • Fix computation of SHA512 checksum (#15736)
  • -
  • Update the script to use quality parameter for dotnet-install (#15731)
  • -
  • Generate SHA512 checksum file for all packages (#15678)
  • -
  • Enable signing daily release build with lifetime certificate (#15642)
  • -
  • Update metadata and README for 7.2.0-preview.7 (#15593)
  • -
- -
- -### Documentation and Help Content - -- Fix broken RFC links (#15807) -- Add to bug report template getting details from `Get-Error` (#15737) -- Update issue templates to link to new docs (#15711) -- Add @jborean93 to Remoting Working Group (#15683) - -[7.2.0-preview.8]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.7...v7.2.0-preview.8 - -## [7.2.0-preview.7] - 2021-06-17 - -### Breaking Changes - -- Remove PSDesiredStateConfiguration v2.0.5 module and published it to the PowerShell Gallery (#15536) - -### Engine Updates and Fixes - -- Fix splatting being treated as positional parameter in completions (#14623) (Thanks @MartinGC94!) -- Prevent PowerShell from crashing when a telemetry mutex can't be created (#15574) (Thanks @gukoff!) -- Ignore all exceptions when disposing an instance of a subsystem implementation (#15511) -- Wait for SSH exit when closing remote connection (#14635) (Thanks @dinhngtu!) - -### Performance - -- Retrieve `ProductVersion` using informational version attribute in `AmsiUtils.Init()` (#15527) (Thanks @Fs00!) - -### General Cmdlet Updates and Fixes - -- Fix retrieving dynamic parameters from provider even if globbed path returns no results (#15525) -- Revert "Enhance Remove-Item to work with OneDrive (#15260)" due to long path issue (#15546) - -### Code Cleanup - -
- - - -

We thank the following contributors!

-

@octos4murai, @iSazonov, @Fs00

- -
- -
    -
  • Correct parameter name passed to exception in PSCommand constructor (#15580) (Thanks @octos4murai!)
  • -
  • Enable nullable: System.Management.Automation.ICommandRuntime (#15566) (Thanks @iSazonov!)
  • -
  • Clean up code regarding AppDomain.CreateDomain and AppDomain.Unload (#15554)
  • -
  • Replace ProcessModule.FileName with Environment.ProcessPath and remove PSUtils.GetMainModule (#15012) (Thanks @Fs00!)
  • -
- -
- -### Tests - -- Fix `Start-Benchmarking` to put `TargetPSVersion` and `TargetFramework` in separate parameter sets (#15508) -- Add `win-x86` test package to the build (#15517) - -### Build and Packaging Improvements - -
- - - -

We thank the following contributors!

-

@schuelermine

- -
- -
    -
  • Update README.md and metadata.json for version 7.2.0-preview.6 (#15464)
  • -
  • Make sure GA revision increases from RC and Preview releases (#15558)
  • -
  • Remove SupportsShouldProcess from Start-PSBootstrap in build.psm1 (#15491) (Thanks @schuelermine!)
  • -
  • Update DotnetMetadataRuntime.json next channel to take daily build from .NET preview 5 (#15518)
  • -
  • Fix deps.json update in the release pipeline (#15486)
  • -
- -
- -### Documentation and Help Content - -- Add new members to Engine and Cmdlet Working Groups document (#15560) -- Update the `mdspell` command to exclude the folder that should be ignored (#15576) -- Replace 'User Voice' with 'Feedback Hub' in `README.md` (#15557) -- Update Virtual User Group chat links (#15505) (Thanks @Jaykul!) -- Fix typo in `FileSystemProvider.cs` (#15445) (Thanks @eltociear!) -- Add `PipelineStoppedException` notes to PowerShell API (#15324) -- Updated governance on Working Groups (WGs) (#14603) -- Correct and improve XML documentation comments on `PSCommand` (#15568) (Thanks @octos4murai!) - -[7.2.0-preview.7]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.6...v7.2.0-preview.7 - -## [7.2.0-preview.6] - 2021-05-27 - -### Experimental Features - -- [Breaking Change] Update prediction interface to provide additional feedback to a predictor plugin (#15421) - -### Performance - -- Avoid collecting logs in buffer if a pipeline execution event is not going to be logged (#15350) -- Avoid allocation in `LanguagePrimitives.UpdateTypeConvertFromTypeTable` (#15168) (Thanks @xtqqczze!) -- Replace `Directory.GetDirectories` with `Directory.EnumerateDirectories` to avoid array allocations (#15167) (Thanks @xtqqczze!) -- Use `List.ConvertAll` instead of `LINQ` (#15140) (Thanks @xtqqczze!) - -### General Cmdlet Updates and Fixes - -- Use `AllocConsole` before initializing CLR to ensure codepage is correct for WinRM remoting (PowerShell/PowerShell-Native#70) (Thanks @jborean93!) -- Add completions for `#requires` statements (#14596) (Thanks @MartinGC94!) -- Add completions for comment-based help keywords (#15337) (Thanks @MartinGC94!) -- Move cross platform DSC code to a PowerShell engine subsystem (#15127) -- Fix `Minimal` progress view to handle activity that is longer than console width (#15264) -- Handle exception if ConsoleHost tries to set cursor out of bounds because screen buffer changed (#15380) -- Fix `NullReferenceException` in DSC `ClearCache()` (#15373) -- Update `ControlSequenceLength` to handle colon as a virtual terminal parameter separator (#14942) -- Update the summary comment for `StopTranscriptCmdlet.cs` (#15349) (Thanks @dbaileyut!) -- Remove the unusable alias `d` for the `-Directory` parameter from `Get-ChildItem` (#15171) (Thanks @kvprasoon!) -- Fix tab completion for un-localized `about` topics (#15265) (Thanks @MartinGC94!) -- Remove the unneeded SSH stdio handle workaround (#15308) -- Add `LoadAssemblyFromNativeMemory` API to load assemblies from memory in a native PowerShell host (#14652) (Thanks @awakecoding!) -- Re-implement `Remove-Item` OneDrive support (#15260) (Thanks @iSazonov!) -- Kill native processes in pipeline when pipeline is disposed on Unix (#15287) -- Default to MTA on Windows platforms where STA is not supported (#15106) - -### Code Cleanup - -
- - - -

We thank the following contributors!

-

@xtqqczze, @powercode, @bcwood

- -
- -
    -
  • Enable nullable in some classes (#14185, #14177, #14159, #14191, #14162, #14150, #14156, #14161, #14155, #14163, #14181, #14157, #14151) (Thanks @powercode!)
  • -
  • Annotate ThrowTerminatingError with DoesNotReturn attribute (#15352) (Thanks @powercode!)
  • -
  • Use GetValueOrDefault() for nullable PSLanguageMode (#13849) (Thanks @bcwood!)
  • -
  • Enable SA1008: Opening parenthesis should be spaced correctly (#14242) (Thanks @xtqqczze!)
  • -
- -
- -### Tools - -- Add `winget` release script (#15050) - -### Tests - -- Enable cross-runtime benchmarking to compare different .NET runtimes (#15387) (Thanks @adamsitnik!) -- Add the performance benchmark project for PowerShell performance testing (#15242) - -### Build and Packaging Improvements - -
- - -Update .NET to version v6.0.0-preview.4 - - -
    -
  • Suppress prompting when uploading the msixbundle package to blob (#15227)
  • -
  • Update to .NET preview 4 SDK (#15452)
  • -
  • Update AppxManifest.xml with newer OS version to allow PowerShell installed from Windows Store to make system-level changes (#15375)
  • -
  • Ensure the build works when PSDesiredStateConfiguration module is pulled in from PSGallery (#15355)
  • -
  • Make sure daily release tag does not change when retrying failures (#15286)
  • -
  • Improve messages and behavior when there's a problem in finding zip files (#15284)
  • -
- -
- -### Documentation and Help Content - -- Add documentation comments section to coding guidelines (#14316) (Thanks @xtqqczze!) - -[7.2.0-preview.6]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.5...v7.2.0-preview.6 - -## [7.2.0-preview.5] - 2021-04-14 - -### Breaking Changes - -- Make PowerShell Linux deb and RPM packages universal (#15109) -- Enforce AppLocker Deny configuration before Execution Policy Bypass configuration (#15035) -- Disallow mixed dash and slash in command line parameter prefix (#15142) (Thanks @davidBar-On!) - -### Experimental Features - -- `PSNativeCommandArgumentPassing`: Use `ArgumentList` for native executable invocation (breaking change) (#14692) - -### Engine Updates and Fixes - -- Add `IArgumentCompleterFactory` for parameterized `ArgumentCompleters` (#12605) (Thanks @powercode!) - -### General Cmdlet Updates and Fixes - -- Fix SSH remoting connection never finishing with misconfigured endpoint (#15175) -- Respect `TERM` and `NO_COLOR` environment variables for `$PSStyle` rendering (#14969) -- Use `ProgressView.Classic` when Virtual Terminal is not supported (#15048) -- Fix `Get-Counter` issue with `-Computer` parameter (#15166) (Thanks @krishnayalavarthi!) -- Fix redundant iteration while splitting lines (#14851) (Thanks @hez2010!) -- Enhance `Remove-Item -Recurse` to work with OneDrive (#14902) (Thanks @iSazonov!) -- Change minimum depth to 0 for `ConvertTo-Json` (#14830) (Thanks @kvprasoon!) -- Allow `Set-Clipboard` to accept empty string (#14579) -- Turn on and off `DECCKM` to modify keyboard mode for Unix native commands to work correctly (#14943) -- Fall back to `CopyAndDelete()` when `MoveTo()` fails due to an `IOException` (#15077) - -### Code Cleanup - -
- - - -

We thank the following contributors!

-

@xtqqczze, @iSazonov, @ZhiZe-ZG

- -
- -
    -
  • Update .NET to 6.0.0-preview.3 (#15221)
  • -
  • Add space before comma to hosting test to fix error reported by SA1001 (#15224)
  • -
  • Add SecureStringHelper.FromPlainTextString helper method for efficient secure string creation (#14124) (Thanks @xtqqczze!)
  • -
  • Use static lambda keyword (#15154) (Thanks @iSazonov!)
  • -
  • Remove unnecessary Array -> List -> Array conversion in ProcessBaseCommand.AllProcesses (#15052) (Thanks @xtqqczze!)
  • -
  • Standardize grammar comments in Parser.cs (#15114) (Thanks @ZhiZe-ZG!)
  • -
  • Enable SA1001: Commas should be spaced correctly (#14171) (Thanks @xtqqczze!)
  • -
  • Refactor MultipleServiceCommandBase.AllServices (#15053) (Thanks @xtqqczze!)
  • -
- -
- -### Tools - -- Use Unix line endings for shell scripts (#15180) (Thanks @xtqqczze!) - -### Tests - -- Add the missing tag in Host Utilities tests (#14983) -- Update `copy-props` version in `package.json` (#15124) - -### Build and Packaging Improvements - -
- - - -

We thank the following contributors!

-

@JustinGrote

- -
- -
    -
  • Fix yarn-lock for copy-props (#15225)
  • -
  • Make package validation regex accept universal Linux packages (#15226)
  • -
  • Bump NJsonSchema from 10.4.0 to 10.4.1 (#15190)
  • -
  • Make MSI and EXE signing always copy to fix daily build (#15191)
  • -
  • Sign internals of EXE package so that it works correctly when signed (#15132)
  • -
  • Bump Microsoft.NET.Test.Sdk from 16.9.1 to 16.9.4 (#15141)
  • -
  • Update daily release tag format to work with new Microsoft Update work (#15164)
  • -
  • Feature: Add Ubuntu 20.04 Support to install-powershell.sh (#15095) (Thanks @JustinGrote!)
  • -
  • Treat rebuild branches like release branches (#15099)
  • -
  • Update WiX to 3.11.2 (#15097)
  • -
  • Bump NJsonSchema from 10.3.11 to 10.4.0 (#15092)
  • -
  • Allow patching of preview releases (#15074)
  • -
  • Bump Newtonsoft.Json from 12.0.3 to 13.0.1 (#15084, #15085)
  • -
  • Update the minSize build package filter to be explicit (#15055)
  • -
  • Bump NJsonSchema from 10.3.10 to 10.3.11 (#14965)
  • -
- -
- -### Documentation and Help Content - -- Merge `7.2.0-preview.4` changes to master (#15056) -- Update `README` and `metadata.json` (#15046) -- Fix broken links for `dotnet` CLI (#14937) - -[7.2.0-preview.5]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.4...v7.2.0-preview.5 - -## [7.2.0-preview.4] - 2021-03-16 - -### Breaking Changes - -- Fix `Get-Date -UFormat` `%G` and `%g` behavior (#14555) (Thanks @brianary!) - -### Engine Updates and Fixes - -- Update engine script signature validation to match `Get-AuthenticodeSignature` logic (#14849) -- Avoid array allocations from `GetDirectories` and `GetFiles` (#14327) (Thanks @xtqqczze!) - -### General Cmdlet Updates and Fixes - -- Add `UseOSCIndicator` setting to enable progress indicator in terminal (#14927) -- Re-enable VT mode on Windows after running command in `ConsoleHost` (#14413) -- Fix `Move-Item` for `FileSystemProvider` to use copy-delete instead of move for DFS paths (#14913) -- Fix `PromptForCredential()` to add `targetName` as domain (#14504) -- Update `Concise` `ErrorView` to not show line information for errors from script module functions (#14912) -- Remove the 32,767 character limit on the environment block for `Start-Process` (#14111) (Thanks @hbuckle!) -- Don't write possible secrets to verbose stream for web cmdlets (#14788) - -### Tools - -- Update `dependabot` configuration to V2 format (#14882) -- Add tooling issue slots in PR template (#14697) - -### Tests - -- Move misplaced test file to tests directory (#14908) (Thanks @MarianoAlipi!) -- Refactor MSI CI (#14753) - -### Build and Packaging Improvements - -
- - -Update .NET to version 6.0.100-preview.2.21155.3 - - -
    -
  • Update .NET to version 6.0.100-preview.2.21155.3 (#15007)
  • -
  • Bump Microsoft.PowerShell.Native to 7.2.0-preview.1 (#15030)
  • -
  • Create MSIX Bundle package in release pipeline (#14982)
  • -
  • Build self-contained minimal size package for Guest Config team (#14976)
  • -
  • Bump XunitXml.TestLogger from 3.0.62 to 3.0.66 (#14993) (Thanks @dependabot[bot]!)
  • -
  • Enable building PowerShell for Apple M1 runtime (#14923)
  • -
  • Fix the variable name in the condition for miscellaneous analysis CI (#14975)
  • -
  • Fix the variable usage in CI yaml (#14974)
  • -
  • Disable running markdown link verification in release build CI (#14971)
  • -
  • Bump Microsoft.CodeAnalysis.CSharp from 3.9.0-3.final to 3.9.0 (#14934) (Thanks @dependabot[bot]!)
  • -
  • Declare which variable group is used for checking the blob in the release build (#14970)
  • -
  • Update metadata and script to enable consuming .NET daily builds (#14940)
  • -
  • Bump NJsonSchema from 10.3.9 to 10.3.10 (#14933) (Thanks @dependabot[bot]!)
  • -
  • Use template that disables component governance for CI (#14938)
  • -
  • Add suppress for nuget multi-feed warning (#14893)
  • -
  • Bump NJsonSchema from 10.3.8 to 10.3.9 (#14926) (Thanks @dependabot[bot]!)
  • -
  • Add exe wrapper to release (#14881)
  • -
  • Bump Microsoft.ApplicationInsights from 2.16.0 to 2.17.0 (#14847)
  • -
  • Bump Microsoft.NET.Test.Sdk from 16.8.3 to 16.9.1 (#14895) (Thanks @dependabot[bot]!)
  • -
  • Bump NJsonSchema from 10.3.7 to 10.3.8 (#14896) (Thanks @dependabot[bot]!)
  • -
  • Disable codesign validation where the file type is not supported (#14885)
  • -
  • Fixing broken Experimental Feature list in powershell.config.json (#14858)
  • -
  • Bump NJsonSchema from 10.3.6 to 10.3.7 (#14855)
  • -
  • Add exe wrapper for Microsoft Update scenarios (#14737)
  • -
  • Install wget on CentOS 7 docker image (#14857)
  • -
  • Fix install-dotnet download (#14856)
  • -
  • Fix Bootstrap step in Windows daily test runs (#14820)
  • -
  • Bump NJsonSchema from 10.3.5 to 10.3.6 (#14818)
  • -
  • Bump NJsonSchema from 10.3.4 to 10.3.5 (#14807)
  • -
- -
- -### Documentation and Help Content - -- Update `README.md` and `metadata.json` for upcoming releases (#14755) -- Merge 7.1.3 and 7.0.6 Change log to master (#15009) -- Update `README` and `metadata.json` for releases (#14997) -- Update ChangeLog for `v7.1.2` release (#14783) -- Update ChangeLog for `v7.0.5` release (#14782) (Internal 14479) - -[7.2.0-preview.4]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.3...v7.2.0-preview.4 - -## [7.2.0-preview.3] - 2021-02-11 - -### Breaking Changes - -- Fix `Get-Date -UFormat %u` behavior to comply with ISO 8601 (#14549) (Thanks @brianary!) - -### Engine Updates and Fixes - -- Together with `PSDesiredStateConfiguration` `v3` module allows `Get-DscResource`, `Invoke-DscResource` and DSC configuration compilation on all platforms, supported by PowerShell (using class-based DSC resources). - -### Performance - -- Avoid array allocations from `Directory.GetDirectories` and `Directory.GetFiles`. (#14326) (Thanks @xtqqczze!) -- Avoid `string.ToLowerInvariant()` from `GetEnvironmentVariableAsBool()` to avoid loading libicu at startup (#14323) (Thanks @iSazonov!) -- Get PowerShell version in `PSVersionInfo` using assembly attribute instead of `FileVersionInfo` (#14332) (Thanks @Fs00!) - -### General Cmdlet Updates and Fixes - -- Suppress `Write-Progress` in `ConsoleHost` if output is redirected and fix tests (#14716) -- Experimental feature `PSAnsiProgress`: Add minimal progress bar using ANSI rendering (#14414) -- Fix web cmdlets to properly construct URI from body when using `-NoProxy` (#14673) -- Update the `ICommandPredictor` to provide more feedback and also make feedback easier to be correlated (#14649) -- Reset color after writing `Verbose`, `Debug`, and `Warning` messages (#14698) -- Fix using variable for nested `ForEach-Object -Parallel` calls (#14548) -- When formatting, if collection is modified, don't fail the entire pipeline (#14438) -- Improve completion of parameters for attributes (#14525) (Thanks @MartinGC94!) -- Write proper error messages for `Get-Command ' '` (#13564) (Thanks @jakekerr!) -- Fix typo in the resource string `ProxyURINotSupplied` (#14526) (Thanks @romero126!) -- Add support to `$PSStyle` for strikethrough and hyperlinks (#14461) -- Fix `$PSStyle` blink codes (#14447) (Thanks @iSazonov!) - -### Code Cleanup - -
- - - -

We thank the following contributors!

-

@xtqqczze, @powercode

- -
- -
    -
  • Fix coding style issues: RCS1215, IDE0090, SA1504, SA1119, RCS1139, IDE0032 (#14356, #14341, #14241, #14204, #14442, #14443) (Thanks @xtqqczze!)
  • -
  • Enable coding style checks: CA2249, CA1052, IDE0076, IDE0077, SA1205, SA1003, SA1314, SA1216, SA1217, SA1213 (#14395, #14483, #14494, #14495, #14441, #14476, #14470, #14471, #14472) (Thanks @xtqqczze!)
  • -
  • Enable nullable in PowerShell codebase (#14160, #14172, #14088, #14154, #14166, #14184, #14178) (Thanks @powercode!)
  • -
  • Use string.Split(char) instead of string.Split(string) (#14465) (Thanks @xtqqczze!)
  • -
  • Use string.Contains(char) overload (#14368) (Thanks @xtqqczze!)
  • -
  • Refactor complex if statements (#14398) (Thanks @xtqqczze!)
  • -
- -
- -### Tools - -- Update script to use .NET 6 build resources (#14705) -- Fix the daily GitHub action (#14711) (Thanks @imba-tjd!) -- GitHub Actions: fix deprecated `::set-env` (#14629) (Thanks @imba-tjd!) -- Update markdown test tools (#14325) (Thanks @RDIL!) -- Upgrade `StyleCopAnalyzers` to `v1.2.0-beta.312` (#14354) (Thanks @xtqqczze!) - -### Tests - -- Remove packaging from daily Windows build (#14749) -- Update link to the Manning book (#14750) -- A separate Windows packaging CI (#14670) -- Update `ini` component version in test `package.json` (#14454) -- Disable `libmi` dependent tests for macOS. (#14446) - -### Build and Packaging Improvements - -
- -
    -
  • Fix the NuGet feed name and URL for .NET 6
  • -
  • Fix third party signing for files in sub-folders (#14751)
  • -
  • Make build script variable an ArrayList to enable Add() method (#14748)
  • -
  • Remove old .NET SDKs to make dotnet restore work with the latest SDK in CI pipeline (#14746)
  • -
  • Remove outdated Linux dependencies (#14688)
  • -
  • Bump .NET SDK version to 6.0.0-preview.1 (#14719)
  • -
  • Bump NJsonSchema to 10.3.4 (#14714)
  • -
  • Update daily GitHub action to allow manual trigger (#14718)
  • -
  • Bump XunitXml.TestLogger to 3.0.62 (#14702)
  • -
  • Make universal deb package based on the deb package specification (#14681)
  • -
  • Add manual release automation steps and improve changelog script (#14445)
  • -
  • Fix release build to upload global tool packages to artifacts (#14620)
  • -
  • Port changes from the PowerShell v7.0.4 release (#14637)
  • -
  • Port changes from the PowerShell v7.1.1 release (#14621)
  • -
  • Updated README and metadata.json (#14401, #14606, #14612)
  • -
  • Do not push nupkg artifacts to MyGet (#14613)
  • -
  • Use one feed in each nuget.config in official builds (#14363)
  • -
  • Fix path signed RPMs are uploaded from in release build (#14424)
  • -
- -
- -### Documentation and Help Content - -- Update distribution support request template to point to .NET 5.0 support document (#14578) -- Remove security GitHub issue template (#14453) -- Add intent for using the Discussions feature in repo (#14399) -- Fix Universal Dashboard to refer to PowerShell Universal (#14437) -- Update document link because of HTTP 301 redirect (#14431) (Thanks @xtqqczze!) - -[7.2.0-preview.3]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.2...v7.2.0-preview.3 - -## [7.2.0-preview.2] - 2020-12-15 - -### Breaking Changes - -- Improve detection of mutable value types (#12495) (Thanks @vexx32!) -- Ensure `-PipelineVariable` is set for all output from script cmdlets (#12766) (Thanks @vexx32!) - -### Experimental Features - -- `PSAnsiRendering`: Enable ANSI formatting via `$PSStyle` and support suppressing ANSI output (#13758) - -### Performance - -- Optimize `IEnumerable` variant of replace operator (#14221) (Thanks @iSazonov!) -- Refactor multiply operation for better performance in two `Microsoft.PowerShell.Commands.Utility` methods (#14148) (Thanks @xtqqczze!) -- Use `Environment.TickCount64` instead of `Datetime.Now` as the random seed for AppLocker test file content (#14283) (Thanks @iSazonov!) -- Avoid unnecessary array allocations when searching in GAC (#14291) (Thanks @xtqqczze!) -- Use `OrdinalIgnoreCase` in `CommandLineParser` (#14303) (Thanks @iSazonov!) -- Use `StringComparison.Ordinal` instead of `StringComparison.CurrentCulture` (#14298) (Thanks @iSazonov!) -- Avoid creating instances of the generated delegate helper class in `-replace` implementation (#14128) - -### General Cmdlet Updates and Fixes - -- Write better error message if config file is broken (#13496) (Thanks @iSazonov!) -- Make AppLocker Enforce mode take precedence over UMCI Audit mode (#14353) -- Add `-SkipLimitCheck` switch to `Import-PowerShellDataFile` (#13672) -- Restrict `New-Object` in NoLanguage mode under lock down (#14140) (Thanks @krishnayalavarthi!) -- The `-Stream` parameter now works with directories (#13941) (Thanks @kyanha!) -- Avoid an exception if file system does not support reparse points (#13634) (Thanks @iSazonov!) -- Enable `CA1012`: Abstract types should not have public constructors (#13940) (Thanks @xtqqczze!) -- Enable `SA1212`: Property accessors should follow order (#14051) (Thanks @xtqqczze!) - -### Code Cleanup - -
- - - -

We thank the following contributors!

-

@xtqqczze, @matthewjdegarmo, @powercode, @Gimly

- -
- -
    -
  • Enable SA1007: Operator keyword should be followed by space (#14130) (Thanks @xtqqczze!)
  • -
  • Expand where alias to Where-Object in Reset-PWSHSystemPath.ps1 (#14113) (Thanks @matthewjdegarmo!)
  • -
  • Fix whitespace issues (#14092) (Thanks @xtqqczze!)
  • -
  • Add StyleCop.Analyzers package (#13963) (Thanks @xtqqczze!)
  • -
  • Enable IDE0041: UseIsNullCheck (#14041) (Thanks @xtqqczze!)
  • -
  • Enable IDE0082: ConvertTypeOfToNameOf (#14042) (Thanks @xtqqczze!)
  • -
  • Remove unnecessary usings part 4 (#14023) (Thanks @xtqqczze!)
  • -
  • Fix PriorityAttribute name (#14094) (Thanks @xtqqczze!)
  • -
  • Enable nullable: System.Management.Automation.Interpreter.IBoxableInstruction (#14165) (Thanks @powercode!)
  • -
  • Enable nullable: System.Management.Automation.Provider.IDynamicPropertyProvider (#14167) (Thanks @powercode!)
  • -
  • Enable nullable: System.Management.Automation.Language.IScriptExtent (#14179) (Thanks @powercode!)
  • -
  • Enable nullable: System.Management.Automation.Language.ICustomAstVisitor2 (#14192) (Thanks @powercode!)
  • -
  • Enable nullable: System.Management.Automation.LanguagePrimitives.IConversionData (#14187) (Thanks @powercode!)
  • -
  • Enable nullable: System.Automation.Remoting.Client.IWSManNativeApiFacade (#14186) (Thanks @powercode!)
  • -
  • Enable nullable: System.Management.Automation.Language.ISupportsAssignment (#14180) (Thanks @powercode!)
  • -
  • Enable nullable: System.Management.Automation.ICommandRuntime2 (#14183) (Thanks @powercode!)
  • -
  • Enable nullable: System.Management.Automation.IOutputProcessingState (#14175) (Thanks @powercode!)
  • -
  • Enable nullable: System.Management.Automation.IJobDebugger (#14174) (Thanks @powercode!)
  • -
  • Enable nullable: System.Management.Automation.Interpreter.IInstructionProvider (#14173) (Thanks @powercode!)
  • -
  • Enable nullable: System.Management.Automation.IHasSessionStateEntryVisibility (#14169) (Thanks @powercode!)
  • -
  • Enable nullable: System.Management.Automation.Tracing.IEtwEventCorrelator (#14168) (Thanks @powercode!)
  • -
  • Fix syntax error in Windows packaging script (#14377)
  • -
  • Remove redundant local assignment in AclCommands (#14358) (Thanks @xtqqczze!)
  • -
  • Enable nullable: System.Management.Automation.Language.IAstPostVisitHandler (#14164) (Thanks @powercode!)
  • -
  • Enable nullable: System.Management.Automation.IModuleAssemblyInitializer (#14158) (Thanks @powercode!)
  • -
  • Use Microsoft.PowerShell.MarkdownRender package from nuget.org (#14090)
  • -
  • Replace GetFiles in TestModuleManifestCommand (#14317) (Thanks @xtqqczze!)
  • -
  • Enable nullable: System.Management.Automation.Provider.IContentWriter (#14152) (Thanks @powercode!)
  • -
  • Simplify getting Encoding in TranscriptionOption.FlushContentToDisk (#13910) (Thanks @Gimly!)
  • -
  • Mark applicable structs as readonly and use in-modifier (#13919) (Thanks @xtqqczze!)
  • -
  • Enable nullable: System.Management.Automation.IArgumentCompleter (#14182) (Thanks @powercode!)
  • -
  • Enable CA1822: Mark private members as static (#13897) (Thanks @xtqqczze!)
  • -
  • Fix IDE0090: Simplify new expression part 6 (#14338) (Thanks @xtqqczze!)
  • -
  • Avoid array allocations from GetDirectories/GetFiles. (#14328) (Thanks @xtqqczze!)
  • -
  • Avoid array allocations from GetDirectories/GetFiles. (#14330) (Thanks @xtqqczze!)
  • -
  • Fix RCS1188: Remove redundant auto-property initialization part 2 (#14262) (Thanks @xtqqczze!)
  • -
  • Enable nullable: System.Management.Automation.Host.IHostSupportsInteractiveSession (#14170) (Thanks @powercode!)
  • -
  • Enable nullable: System.Management.Automation.Provider.IPropertyCmdletProvider (#14176) (Thanks @powercode!)
  • -
  • Fix IDE0090: Simplify new expression part 5 (#14301) (Thanks @xtqqczze!)
  • -
  • Enable IDE0075: SimplifyConditionalExpression (#14078) (Thanks @xtqqczze!)
  • -
  • Remove unnecessary usings part 9 (#14288) (Thanks @xtqqczze!)
  • -
  • Fix StyleCop and MarkdownLint CI failures (#14297) (Thanks @xtqqczze!)
  • -
  • Enable SA1000: Keywords should be spaced correctly (#13973) (Thanks @xtqqczze!)
  • -
  • Fix RCS1188: Remove redundant auto-property initialization part 1 (#14261) (Thanks @xtqqczze!)
  • -
  • Mark private members as static part 10 (#14235) (Thanks @xtqqczze!)
  • -
  • Mark private members as static part 9 (#14234) (Thanks @xtqqczze!)
  • -
  • Fix SA1642 for Microsoft.Management.Infrastructure.CimCmdlets (#14239) (Thanks @xtqqczze!)
  • -
  • Use AsSpan/AsMemory slice constructor (#14265) (Thanks @xtqqczze!)
  • -
  • Fix IDE0090: Simplify new expression part 4.6 (#14260) (Thanks @xtqqczze!)
  • -
  • Fix IDE0090: Simplify new expression part 4.5 (#14259) (Thanks @xtqqczze!)
  • -
  • Fix IDE0090: Simplify new expression part 4.3 (#14257) (Thanks @xtqqczze!)
  • -
  • Fix IDE0090: Simplify new expression part 4.2 (#14256) (Thanks @xtqqczze!)
  • -
  • Fix IDE0090: Simplify new expression part 2 (#14200) (Thanks @xtqqczze!)
  • -
  • Enable SA1643: Destructor summary documentation should begin with standard text (#14236) (Thanks @xtqqczze!)
  • -
  • Fix IDE0090: Simplify new expression part 4.4 (#14258) (Thanks @xtqqczze!)
  • -
  • Use xml documentation child blocks correctly (#14249) (Thanks @xtqqczze!)
  • -
  • Fix IDE0090: Simplify new expression part 4.1 (#14255) (Thanks @xtqqczze!)
  • -
  • Use consistent spacing in xml documentation tags (#14231) (Thanks @xtqqczze!)
  • -
  • Enable IDE0074: Use coalesce compound assignment (#13396) (Thanks @xtqqczze!)
  • -
  • Remove unnecessary finalizers (#14248) (Thanks @xtqqczze!)
  • -
  • Mark local variable as const (#13217) (Thanks @xtqqczze!)
  • -
  • Fix IDE0032: UseAutoProperty part 2 (#14244) (Thanks @xtqqczze!)
  • -
  • Fix IDE0032: UseAutoProperty part 1 (#14243) (Thanks @xtqqczze!)
  • -
  • Mark private members as static part 8 (#14233) (Thanks @xtqqczze!)
  • -
  • Fix CA1822: Mark members as static part 6 (#14229) (Thanks @xtqqczze!)
  • -
  • Fix CA1822: Mark members as static part 5 (#14228) (Thanks @xtqqczze!)
  • -
  • Fix CA1822: Mark members as static part 4 (#14227) (Thanks @xtqqczze!)
  • -
  • Fix CA1822: Mark members as static part 3 (#14226) (Thanks @xtqqczze!)
  • -
  • Fix CA1822: Mark members as static part 2 (#14225) (Thanks @xtqqczze!)
  • -
  • Fix CA1822: Mark members as static part 1 (#14224) (Thanks @xtqqczze!)
  • -
  • Use see keyword in documentation (#14220) (Thanks @xtqqczze!)
  • -
  • Enable CA2211: Non-constant fields should not be visible (#14073) (Thanks @xtqqczze!)
  • -
  • Enable CA1816: Dispose methods should call SuppressFinalize (#14074) (Thanks @xtqqczze!)
  • -
  • Remove incorrectly implemented finalizer (#14246) (Thanks @xtqqczze!)
  • -
  • Fix CA1822: Mark members as static part 7 (#14230) (Thanks @xtqqczze!)
  • -
  • Fix SA1122: Use string.Empty for empty strings (#14218) (Thanks @xtqqczze!)
  • -
  • Fix various xml documentation issues (#14223) (Thanks @xtqqczze!)
  • -
  • Remove unnecessary usings part 8 (#14072) (Thanks @xtqqczze!)
  • -
  • Enable SA1006: Preprocessor keywords should not be preceded by space (#14052) (Thanks @xtqqczze!)
  • -
  • Fix SA1642 for Microsoft.PowerShell.Commands.Utility (#14142) (Thanks @xtqqczze!)
  • -
  • Enable CA2216: Disposable types should declare finalizer (#14089) (Thanks @xtqqczze!)
  • -
  • Wrap and name LoadBinaryModule arguments (#14193) (Thanks @xtqqczze!)
  • -
  • Wrap and name GetListOfFilesFromData arguments (#14194) (Thanks @xtqqczze!)
  • -
  • Enable SA1002: Semicolons should be spaced correctly (#14197) (Thanks @xtqqczze!)
  • -
  • Fix IDE0090: Simplify new expression part 3 (#14201) (Thanks @xtqqczze!)
  • -
  • Enable SA1106: Code should not contain empty statements (#13964) (Thanks @xtqqczze!)
  • -
  • Code performance fixes follow-up (#14207) (Thanks @xtqqczze!)
  • -
  • Remove uninformative comments (#14199) (Thanks @xtqqczze!)
  • -
  • Fix IDE0090: Simplify new expression part 1 (#14027) (Thanks @xtqqczze!)
  • -
  • Enable SA1517: Code should not contain blank lines at start of file (#14131) (Thanks @xtqqczze!)
  • -
  • Enable SA1131: Use readable conditions (#14132) (Thanks @xtqqczze!)
  • -
  • Enable SA1507: Code should not contain multiple blank lines in a row (#14136) (Thanks @xtqqczze!)
  • -
  • Enable SA1516 Elements should be separated by blank line (#14137) (Thanks @xtqqczze!)
  • -
  • Enable IDE0031: Null check can be simplified (#13548) (Thanks @xtqqczze!)
  • -
  • Enable CA1065: Do not raise exceptions in unexpected locations (#14117) (Thanks @xtqqczze!)
  • -
  • Enable CA1000: Do not declare static members on generic types (#14097) (Thanks @xtqqczze!)
  • -
- -
- -### Tools - -- Fixing formatting in `Reset-PWSHSystemPath.ps1` (#13689) (Thanks @dgoldman-msft!) - -### Tests - -- Reinstate `Test-Connection` tests (#13324) -- Update markdown test packages with security fixes (#14145) - -### Build and Packaging Improvements - -
- -
    -
  • Fix a typo in the Get-ChangeLog function (#14129)
  • -
  • Update README and metadata.json for 7.2.0-preview.1 release (#14104)
  • -
  • Bump NJsonSchema from 10.2.2 to 10.3.1 (#14040)
  • -
  • Move windows package signing to use ESRP (#14060)
  • -
  • Use one feed in each nuget.config in official builds (#14363)
  • -
  • Fix path signed RPMs are uploaded from in release build (#14424)
  • -
  • Add Microsoft.PowerShell.MarkdownRender to the package reference list (#14386)
  • -
  • Fix issue with unsigned build (#14367)
  • -
  • Move macOS and nuget to ESRP signing (#14324)
  • -
  • Fix nuget packaging to scrub NullableAttribute (#14344)
  • -
  • Bump Microsoft.NET.Test.Sdk from 16.8.0 to 16.8.3 (#14310)
  • -
  • Bump Markdig.Signed from 0.22.0 to 0.22.1 (#14305)
  • -
  • Bump Microsoft.ApplicationInsights from 2.15.0 to 2.16.0 (#14031)
  • -
  • Move Linux to ESRP signing (#14210)
  • -
- -
- -### Documentation and Help Content - -- Fix example `nuget.config` (#14349) -- Fix a broken link in Code Guidelines doc (#14314) (Thanks @iSazonov!) - -[7.2.0-preview.2]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.1...v7.2.0-preview.2 - -## [7.2.0-preview.1] - 2020-11-17 - -### Engine Updates and Fixes - -- Change the default fallback encoding for `GetEncoding` in `Start-Transcript` to be `UTF8` without a BOM (#13732) (Thanks @Gimly!) - -### General Cmdlet Updates and Fixes - -- Update `pwsh -?` output to match docs (#13748) -- Fix `NullReferenceException` in `Test-Json` (#12942) (Thanks @iSazonov!) -- Make `Dispose` in `TranscriptionOption` idempotent (#13839) (Thanks @krishnayalavarthi!) -- Add additional Microsoft PowerShell modules to the tracked modules list (#12183) -- Relax further `SSL` verification checks for `WSMan` on non-Windows hosts with verification available (#13786) (Thanks @jborean93!) -- Add the `OutputTypeAttribute` to `Get-ExperimentalFeature` (#13738) (Thanks @ThomasNieto!) -- Fix blocking wait when starting file associated with a Windows application (#13750) -- Emit warning if `ConvertTo-Json` exceeds `-Depth` value (#13692) - -### Code Cleanup - -
- - - -

We thank the following contributors!

-

@xtqqczze, @mkswd, @ThomasNieto, @PatLeong, @paul-cheung, @georgettica

- -
- -
    -
  • Fix RCS1049: Simplify boolean comparison (#13994) (Thanks @xtqqczze!)
  • -
  • Enable IDE0062: Make local function static (#14044) (Thanks @xtqqczze!)
  • -
  • Enable CA2207: Initialize value type static fields inline (#14068) (Thanks @xtqqczze!)
  • -
  • Enable CA1837: Use ProcessId and CurrentManagedThreadId from System.Environment (#14063) (Thanks @xtqqczze and @PatLeong!)
  • -
  • Remove unnecessary using directives (#14014, #14017, #14021, #14050, #14065, #14066, #13863, #13860, #13861, #13814) (Thanks @xtqqczze and @ThomasNieto!)
  • -
  • Remove unnecessary usage of LINQ Count method (#13545) (Thanks @xtqqczze!)
  • -
  • Fix SA1518: The code must not contain extra blank lines at the end of the file (#13574) (Thanks @xtqqczze!)
  • -
  • Enable CA1829: Use the Length or Count property instead of Count() (#13925) (Thanks @xtqqczze!)
  • -
  • Enable CA1827: Do not use Count() or LongCount() when Any() can be used (#13923) (Thanks @xtqqczze!)
  • -
  • Enable or fix nullable usage in a few files (#13793, #13805, #13808, #14018, #13804) (Thanks @mkswd and @georgettica!)
  • -
  • Enable IDE0040: Add accessibility modifiers (#13962, #13874) (Thanks @xtqqczze!)
  • -
  • Make applicable private Guid fields readonly (#14000) (Thanks @xtqqczze!)
  • -
  • Fix CA1003: Use generic event handler instances (#13937) (Thanks @xtqqczze!)
  • -
  • Simplify delegate creation (#13578) (Thanks @xtqqczze!)
  • -
  • Fix RCS1033: Remove redundant boolean literal (#13454) (Thanks @xtqqczze!)
  • -
  • Fix RCS1221: Use pattern matching instead of combination of as operator and null check (#13333) (Thanks @xtqqczze!)
  • -
  • Use is not syntax (#13338) (Thanks @xtqqczze!)
  • -
  • Replace magic number with constant in PDH (#13536) (Thanks @xtqqczze!)
  • -
  • Fix accessor order (#13538) (Thanks @xtqqczze!)
  • -
  • Enable IDE0054: Use compound assignment (#13546) (Thanks @xtqqczze!)
  • -
  • Fix RCS1098: Constant values should be on right side of comparisons (#13833) (Thanks @xtqqczze!)
  • -
  • Enable CA1068: CancellationToken parameters must come last (#13867) (Thanks @xtqqczze!)
  • -
  • Enable CA10XX rules with suggestion severity (#13870, #13928, #13924) (Thanks @xtqqczze!)
  • -
  • Enable IDE0064: Make Struct fields writable (#13945) (Thanks @xtqqczze!)
  • -
  • Run dotnet-format to improve formatting of source code (#13503) (Thanks @xtqqczze!)
  • -
  • Enable CA1825: Avoid zero-length array allocations (#13961) (Thanks @xtqqczze!)
  • -
  • Add IDE analyzer rule IDs to comments (#13960) (Thanks @xtqqczze!)
  • -
  • Enable CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder (#13926) (Thanks @xtqqczze!)
  • -
  • Enforce code style in build (#13957) (Thanks @xtqqczze!)
  • -
  • Enable CA1836: Prefer IsEmpty over Count when available (#13877) (Thanks @xtqqczze!)
  • -
  • Enable CA1834: Consider using StringBuilder.Append(char) when applicable (#13878) (Thanks @xtqqczze!)
  • -
  • Fix IDE0044: Make field readonly (#13884, #13885, #13888, #13892, #13889, #13886, #13890, #13891, #13887, #13893, #13969, #13967, #13968, #13970, #13971, #13966, #14012) (Thanks @xtqqczze!)
  • -
  • Enable IDE0048: Add required parentheses (#13896) (Thanks @xtqqczze!)
  • -
  • Enable IDE1005: Invoke delegate with conditional access (#13911) (Thanks @xtqqczze!)
  • -
  • Enable IDE0036: Enable the check on the order of modifiers (#13958, #13881) (Thanks @xtqqczze!)
  • -
  • Use span-based String.Concat instead of String.Substring (#13500) (Thanks @xtqqczze!)
  • -
  • Enable CA1050: Declare types in namespace (#13872) (Thanks @xtqqczze!)
  • -
  • Fix minor keyword typo in C# code comment (#13811) (Thanks @paul-cheung!)
  • -
- -
- -### Tools - -- Enable `CodeQL` Security scanning (#13894) -- Add global `AnalyzerConfig` with default configuration (#13835) (Thanks @xtqqczze!) - -### Build and Packaging Improvements - -
- - - -

We thank the following contributors!

-

@mkswd, @xtqqczze

- -
- -
    -
  • Bump Microsoft.NET.Test.Sdk to 16.8.0 (#14020)
  • -
  • Bump Microsoft.CodeAnalysis.CSharp to 3.8.0 (#14075)
  • -
  • Remove workarounds for .NET 5 RTM builds (#14038)
  • -
  • Migrate 3rd party signing to ESRP (#14010)
  • -
  • Fixes to release pipeline for GA release (#14034)
  • -
  • Don't do a shallow checkout (#13992)
  • -
  • Add validation and dependencies for Ubuntu 20.04 distribution to packaging script (#13993)
  • -
  • Add .NET install workaround for RTM (#13991)
  • -
  • Move to ESRP signing for Windows files (#13988)
  • -
  • Update PSReadLine version to 2.1.0 (#13975)
  • -
  • Bump .NET to version 5.0.100-rtm.20526.5 (#13920)
  • -
  • Update script to use .NET RTM feeds (#13927)
  • -
  • Add checkout step to release build templates (#13840)
  • -
  • Turn on /features:strict for all projects (#13383) (Thanks @xtqqczze!)
  • -
  • Bump NJsonSchema to 10.2.2 (#13722, #13751)
  • -
  • Add flag to make Linux script publish to production repo (#13714)
  • -
  • Bump Markdig.Signed to 0.22.0 (#13741)
  • -
  • Use new release script for Linux packages (#13705)
  • -
- -
- -### Documentation and Help Content - -- Fix links to LTS versions for Windows (#14070) -- Fix `crontab` formatting in example doc (#13712) (Thanks @dgoldman-msft!) - -[7.2.0-preview.1]: https://github.com/PowerShell/PowerShell/compare/v7.1.0...v7.2.0-preview.1 diff --git a/DotnetRuntimeMetadata.json b/DotnetRuntimeMetadata.json index 7a24b57b192..0638b5aa502 100644 --- a/DotnetRuntimeMetadata.json +++ b/DotnetRuntimeMetadata.json @@ -1,13 +1,15 @@ { "sdk": { - "channel": "6.0.1xx-rc1", - "quality": "signed", - "qualityFallback": "daily", - "packageVersionPattern": "6.0.0-rc.1", - "sdkImageVersion": "6.0.100", - "nextChannel": "6.0.1xx-rc1" + "channel": "7.0.1xx", + "quality": "daily", + "qualityFallback": "preview", + "packageVersionPattern": "7.0.0", + "sdkImageVersion": "7.0.102", + "nextChannel": "7.0.1xx-rc2", + "azureFeed": "", + "sdkImageOverride": "" }, - "internalfeed" : { - "url": "https://pkgs.dev.azure.com/dnceng/public/_packaging/6.0.100-rc.1.21458.32-shipping/nuget/v2" + "internalfeed": { + "url": "" } } diff --git a/PowerShell.Common.props b/PowerShell.Common.props index ba5e6e2dffe..5e7e41f1470 100644 --- a/PowerShell.Common.props +++ b/PowerShell.Common.props @@ -135,9 +135,8 @@ Microsoft Corporation (c) Microsoft Corporation. - net6.0 + net7.0 10.0 - true true true @@ -170,11 +169,14 @@ + true + true true + portable - - + + true full @@ -186,13 +188,6 @@ Debugging the issues resolves the problem --> false - portable - - - - - - portable diff --git a/README.md b/README.md index d30953d7d5b..49061b9d33a 100644 --- a/README.md +++ b/README.md @@ -30,19 +30,19 @@ You can download and install a PowerShell package for any of the following platf | -------------------------------------------| ------------------------| ------------------------| ----------------------| ------------------------------| | [Windows (x64)][corefx-win] | [.msi][lts-windows-64] | [.msi][rl-windows-64] | [.msi][pv-windows-64] | [Instructions][in-windows] | | [Windows (x86)][corefx-win] | [.msi][lts-windows-86] | [.msi][rl-windows-86] | [.msi][pv-windows-86] | [Instructions][in-windows] | -| [Ubuntu 20.04][corefx-linux] | | [.deb][rl-ubuntu20] | [.deb][pv-deb] | [Instructions][in-ubuntu20] | -| [Ubuntu 18.04][corefx-linux] | [.deb][lts-ubuntu18] | [.deb][rl-ubuntu18] | [.deb][pv-deb] | [Instructions][in-ubuntu18] | -| [Ubuntu 16.04][corefx-linux] | [.deb][lts-ubuntu16] | [.deb][rl-ubuntu16] | [.deb][pv-deb] | [Instructions][in-ubuntu16] | -| [Debian 9][corefx-linux] | [.deb][lts-debian9] | [.deb][rl-debian9] | [.deb][pv-deb] | [Instructions][in-deb9] | -| [Debian 10][corefx-linux] | [.deb][lts-debian10] | [.deb][rl-debian10] | [.deb][pv-deb] | [Instructions][in-deb9] | -| [Debian 11][corefx-linux] | | [.deb][rl-debian11] | [.deb][pv-deb] | | -| [CentOS 7][corefx-linux] | [.rpm][lts-centos] | [.rpm][rl-centos] | [.rpm][pv-rpm] | [Instructions][in-centos] | -| [CentOS 8][corefx-linux] | [.rpm][lts-centos8] | [.rpm][rl-centos8] | [.rpm][pv-rpm] | | -| [Red Hat Enterprise Linux 7][corefx-linux] | [.rpm][lts-centos] | [.rpm][rl-centos] | [.rpm][pv-rpm] | [Instructions][in-rhel7] | -| [openSUSE 42.3][corefx-linux] | [.rpm][lts-centos] | [.rpm][rl-centos] | [.rpm][pv-rpm] | [Instructions][in-opensuse] | -| [Fedora 30][corefx-linux] | [.rpm][lts-centos] | [.rpm][rl-centos] | [.rpm][pv-rpm] | [Instructions][in-fedora] | +| [Ubuntu 20.04][corefx-linux] | [.deb][lts-deb] | [.deb][rl-ubuntu20] | [.deb][pv-deb] | [Instructions][in-ubuntu20] | +| [Ubuntu 18.04][corefx-linux] | [.deb][lts-deb] | [.deb][rl-ubuntu18] | [.deb][pv-deb] | [Instructions][in-ubuntu18] | +| [Ubuntu 16.04][corefx-linux] | [.deb][lts-deb] | [.deb][rl-ubuntu16] | [.deb][pv-deb] | [Instructions][in-ubuntu16] | +| [Debian 9][corefx-linux] | [.deb][lts-deb] | [.deb][rl-debian9] | [.deb][pv-deb] | [Instructions][in-deb9] | +| [Debian 10][corefx-linux] | [.deb][lts-deb] | [.deb][rl-debian10] | [.deb][pv-deb] | [Instructions][in-deb9] | +| [Debian 11][corefx-linux] | [.deb][lts-deb] | [.deb][rl-debian11] | [.deb][pv-deb] | | +| [CentOS 7][corefx-linux] | [.rpm][lts-rh] | [.rpm][rl-centos] | [.rpm][pv-rpm] | [Instructions][in-centos] | +| [CentOS 8][corefx-linux] | [.rpm][lts-rh] | [.rpm][rl-centos8] | [.rpm][pv-rpm] | | +| [Red Hat Enterprise Linux 7][corefx-linux] | [.rpm][lts-rh] | [.rpm][rl-centos] | [.rpm][pv-rpm] | [Instructions][in-rhel7] | +| [openSUSE 42.3][corefx-linux] | [.rpm][lts-rh] | [.rpm][rl-centos] | [.rpm][pv-rpm] | [Instructions][in-opensuse] | +| [Fedora 35][corefx-linux] | [.rpm][lts-rh] | [.rpm][rl-centos] | [.rpm][pv-rpm] | [Instructions][in-fedora] | | [macOS 10.13+ (x64)][corefx-macos] | [.pkg][lts-macos] | [.pkg][rl-macos] | [.pkg][pv-macos] | [Instructions][in-macos] | -| [macOS 10.13+ (arm64)][corefx-macos] | | | [.pkg][pv-macos-arm64]| [Instructions][in-macos] | +| [macOS 10.13+ (arm64)][corefx-macos] | [.pkg][lts-macos-arm64] | [.pkg][rl-macos-arm64] | [.pkg][pv-macos-arm64]| [Instructions][in-macos] | | Docker | | | | [Instructions][in-docker] | You can download and install a PowerShell package for any of the following platforms, **which are supported by the community.** @@ -58,56 +58,55 @@ You can also download the PowerShell binary archives for Windows, macOS and Linu | Platform | Downloads (stable) | Downloads (preview) | How to Install | | ---------------| --------------------------------------------------- | ------------------------------------------------| -----------------------------------------------| | Windows | [32-bit][rl-winx86-zip]/[64-bit][rl-winx64-zip] | [32-bit][pv-winx86-zip]/[64-bit][pv-winx64-zip] | [Instructions][in-windows-zip] | -| macOS | [64-bit][rl-macos-tar] | [64-bit][pv-macos-tar] | [Instructions][in-tar-macos] | -| macOS | | [64-bit][pv-macos-tar-arm64] | [Instructions][in-tar-macos] | +| macOS (x64) | [64-bit][rl-macos-tar] | [64-bit][pv-macos-tar] | [Instructions][in-tar-macos] | +| macOS (arm64) | [64-bit][rl-macos-tar-arm64] | [64-bit][pv-macos-tar-arm64] | [Instructions][in-tar-macos] | | Linux | [64-bit][rl-linux-tar] | [64-bit][pv-linux-tar] | [Instructions][in-tar-linux] | | Windows (Arm) | [64-bit][rl-winarm64] (preview) | [64-bit][pv-winarm64] | [Instructions][in-arm] | | Raspbian (Arm) | [32-bit][rl-arm32]/[64-bit][rl-arm64] | [32-bit][pv-arm32]/[64-bit][pv-arm64] | [Instructions][in-raspbian] | -[lts-windows-86]: https://github.com/PowerShell/PowerShell/releases/download/v7.0.7/PowerShell-7.0.7-win-x86.msi -[lts-windows-64]: https://github.com/PowerShell/PowerShell/releases/download/v7.0.7/PowerShell-7.0.7-win-x64.msi -[lts-ubuntu18]: https://github.com/PowerShell/PowerShell/releases/download/v7.0.7/powershell-lts_7.0.7-1.ubuntu.18.04_amd64.deb -[lts-ubuntu16]: https://github.com/PowerShell/PowerShell/releases/download/v7.0.7/powershell-lts_7.0.7-1.ubuntu.16.04_amd64.deb -[lts-debian9]: https://github.com/PowerShell/PowerShell/releases/download/v7.0.7/powershell-lts_7.0.7-1.debian.9_amd64.deb -[lts-debian10]: https://github.com/PowerShell/PowerShell/releases/download/v7.0.7/powershell-lts_7.0.7-1.debian.10_amd64.deb -[lts-centos]: https://github.com/PowerShell/PowerShell/releases/download/v7.0.7/powershell-lts-7.0.7-1.rhel.7.x86_64.rpm -[lts-centos8]: https://github.com/PowerShell/PowerShell/releases/download/v7.0.7/powershell-lts-7.0.7-1.centos.8.x86_64.rpm -[lts-macos]: https://github.com/PowerShell/PowerShell/releases/download/v7.0.7/powershell-lts-7.0.7-osx-x64.pkg - -[rl-windows-64]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/PowerShell-7.1.4-win-x64.msi -[rl-windows-86]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/PowerShell-7.1.4-win-x86.msi -[rl-ubuntu20]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/powershell_7.1.4-1.ubuntu.20.04_amd64.deb -[rl-ubuntu18]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/powershell_7.1.4-1.ubuntu.18.04_amd64.deb -[rl-ubuntu16]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/powershell_7.1.4-1.ubuntu.16.04_amd64.deb -[rl-debian9]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/powershell_7.1.4-1.debian.9_amd64.deb -[rl-debian10]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/powershell_7.1.4-1.debian.10_amd64.deb -[rl-debian11]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/powershell_7.1.4-1.debian.11_amd64.deb -[rl-centos]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/powershell-7.1.4-1.rhel.7.x86_64.rpm -[rl-centos8]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/powershell-7.1.4-1.centos.8.x86_64.rpm -[rl-macos]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/powershell-7.1.4-osx-x64.pkg -[rl-winarm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/PowerShell-7.1.4-win-arm64.zip -[rl-winx86-zip]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/PowerShell-7.1.4-win-x86.zip -[rl-winx64-zip]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/PowerShell-7.1.4-win-x64.zip -[rl-macos-tar]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/powershell-7.1.4-osx-x64.tar.gz -[rl-linux-tar]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/powershell-7.1.4-linux-x64.tar.gz -[rl-arm32]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/powershell-7.1.4-linux-arm32.tar.gz -[rl-arm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.1.4/powershell-7.1.4-linux-arm64.tar.gz +[lts-windows-86]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/PowerShell-7.2.6-win-x86.msi +[lts-windows-64]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/PowerShell-7.2.6-win-x64.msi +[lts-deb]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell-lts_7.2.6-1.deb_amd64.deb +[lts-rh]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell-lts-7.2.6-1.rh.x86_64.rpm +[lts-macos]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell-lts-7.2.6-osx-x64.pkg +[lts-macos-arm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell-lts-7.2.6-osx-arm64.pkg + +[rl-windows-64]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/PowerShell-7.2.6-win-x64.msi +[rl-windows-86]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/PowerShell-7.2.6-win-x86.msi +[rl-ubuntu20]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell_7.2.6-1.deb_amd64.deb +[rl-ubuntu18]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell_7.2.6-1.deb_amd64.deb +[rl-ubuntu16]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell_7.2.6-1.deb_amd64.deb +[rl-debian9]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell_7.2.6-1.deb_amd64.deb +[rl-debian10]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell_7.2.6-1.deb_amd64.deb +[rl-debian11]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell_7.2.6-1.deb_amd64.deb +[rl-centos]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell-7.2.6-1.rh.x86_64.rpm +[rl-centos8]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell-7.2.6-1.rh.x86_64.rpm +[rl-macos]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell-7.2.6-osx-x64.pkg +[rl-macos-arm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell-7.2.6-osx-arm64.pkg +[rl-winarm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/PowerShell-7.2.6-win-arm64.zip +[rl-winx86-zip]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/PowerShell-7.2.6-win-x86.zip +[rl-winx64-zip]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/PowerShell-7.2.6-win-x64.zip +[rl-macos-tar]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell-7.2.6-osx-x64.tar.gz +[rl-macos-tar-arm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell-7.2.6-osx-arm64.tar.gz +[rl-linux-tar]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell-7.2.6-linux-x64.tar.gz +[rl-arm32]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell-7.2.6-linux-arm32.tar.gz +[rl-arm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell-7.2.6-linux-arm64.tar.gz [rl-snap]: https://snapcraft.io/powershell -[pv-windows-64]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.0-preview.8/PowerShell-7.2.0-preview.8-win-x64.msi -[pv-windows-86]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.0-preview.8/PowerShell-7.2.0-preview.8-win-x86.msi -[pv-deb]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.0-preview.8/powershell-preview_7.2.0-preview.8-1.deb_amd64.deb -[pv-rpm]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.0-preview.8/powershell-preview-7.2.0_preview.8-1.rh.x86_64.rpm -[pv-macos]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.0-preview.8/powershell-7.2.0-preview.8-osx-x64.pkg -[pv-macos-arm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.0-preview.8/powershell-7.2.0-preview.8-osx-arm64.pkg -[pv-winarm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.0-preview.8/PowerShell-7.2.0-preview.8-win-arm64.zip -[pv-winx86-zip]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.0-preview.8/PowerShell-7.2.0-preview.8-win-x86.zip -[pv-winx64-zip]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.0-preview.8/PowerShell-7.2.0-preview.8-win-x64.zip -[pv-macos-tar]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.0-preview.8/powershell-7.2.0-preview.8-osx-x64.tar.gz -[pv-macos-tar-arm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.0-preview.8/powershell-7.2.0-preview.8-osx-arm64.tar.gz -[pv-linux-tar]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.0-preview.8/powershell-7.2.0-preview.8-linux-x64.tar.gz -[pv-arm32]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.0-preview.8/powershell-7.2.0-preview.8-linux-arm32.tar.gz -[pv-arm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.2.0-preview.8/powershell-7.2.0-preview.8-linux-arm64.tar.gz +[pv-windows-64]: https://github.com/PowerShell/PowerShell/releases/download/v7.3.0-preview.7/PowerShell-7.3.0-preview.7-win-x64.msi +[pv-windows-86]: https://github.com/PowerShell/PowerShell/releases/download/v7.3.0-preview.7/PowerShell-7.3.0-preview.7-win-x86.msi +[pv-deb]: https://github.com/PowerShell/PowerShell/releases/download/v7.3.0-preview.7/powershell-preview_7.3.0-preview.7-1.deb_amd64.deb +[pv-rpm]: https://github.com/PowerShell/PowerShell/releases/download/v7.3.0-preview.7/powershell-preview-7.3.0_preview.7-1.rh.x86_64.rpm +[pv-macos]: https://github.com/PowerShell/PowerShell/releases/download/v7.3.0-preview.7/powershell-7.3.0-preview.7-osx-x64.pkg +[pv-macos-arm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.3.0-preview.7/powershell-7.3.0-preview.7-osx-arm64.pkg +[pv-winarm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.3.0-preview.7/PowerShell-7.3.0-preview.7-win-arm64.zip +[pv-winx86-zip]: https://github.com/PowerShell/PowerShell/releases/download/v7.3.0-preview.7/PowerShell-7.3.0-preview.7-win-x86.zip +[pv-winx64-zip]: https://github.com/PowerShell/PowerShell/releases/download/v7.3.0-preview.7/PowerShell-7.3.0-preview.7-win-x64.zip +[pv-macos-tar]: https://github.com/PowerShell/PowerShell/releases/download/v7.3.0-preview.7/powershell-7.3.0-preview.7-osx-x64.tar.gz +[pv-macos-tar-arm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.3.0-preview.7/powershell-7.3.0-preview.7-osx-arm64.tar.gz +[pv-linux-tar]: https://github.com/PowerShell/PowerShell/releases/download/v7.3.0-preview.7/powershell-7.3.0-preview.7-linux-x64.tar.gz +[pv-arm32]: https://github.com/PowerShell/PowerShell/releases/download/v7.3.0-preview.7/powershell-7.3.0-preview.7-linux-arm32.tar.gz +[pv-arm64]: https://github.com/PowerShell/PowerShell/releases/download/v7.3.0-preview.7/powershell-7.3.0-preview.7-linux-arm64.tar.gz [pv-snap]: https://snapcraft.io/powershell-preview [in-windows]: https://docs.microsoft.com/powershell/scripting/install/installing-powershell-core-on-windows @@ -143,7 +142,7 @@ For more information on how and why we built this dashboard, check out this [blo ## Discussions -[GitHub Discussions](https://docs.github.com/en/free-pro-team@latest/discussions/quickstart) is a feature to enable fluid and open discussions within the community +[GitHub Discussions](https://docs.github.com/discussions/quickstart) is a feature to enable fluid and open discussions within the community for topics that are not related to code, unlike issues. This is an experiment we are trying in our repositories to see if it helps move discussions out of issues so that issues remain actionable by the team or members of the community. @@ -240,9 +239,8 @@ License: By requesting and using the Container OS Image for Windows containers, ### Telemetry -By default, PowerShell collects the OS description and the version of PowerShell (equivalent to `$PSVersionTable.OS` and `$PSVersionTable.GitCommitId`) using [Application Insights](https://azure.microsoft.com/services/application-insights/). -To opt-out of sending telemetry, create an environment variable called `POWERSHELL_TELEMETRY_OPTOUT` set to a value of `1` before starting PowerShell from the installed location. -The telemetry we collect falls under the [Microsoft Privacy Statement](https://privacy.microsoft.com/privacystatement/). +Please visit our [about_Telemetry](https://docs.microsoft.com/powershell/module/microsoft.powershell.core/about/about_telemetry) +topic to read details about telemetry gathered by PowerShell. ## Governance diff --git a/Settings.StyleCop b/Settings.StyleCop index 7fa179ee02e..e10c02bdd12 100644 --- a/Settings.StyleCop +++ b/Settings.StyleCop @@ -162,6 +162,7 @@ op my sb + vt diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 06747f655ca..1ba383a1999 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -15,251 +15,301 @@ USA Notwithstanding any other terms, you may reverse engineer this software to the extent required to debug changes to any libraries licensed under the GNU Lesser General Public License. +--------------------------------------------------------- + +Markdig.Signed 0.30.4 - BSD-2-Clause -------------------------------------------------------------------- -Microsoft.CodeAnalysis.Common 3.3.1 - Apache-2.0 -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. -Apache License +Copyright (c) . All rights reserved. -Version 2.0, January 2004 +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - 1. Definitions. + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. +--------------------------------------------------------- - +--------------------------------------------------------- - "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. +Microsoft.ApplicationInsights 2.21.0 - MIT - - "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - +MIT License - "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. +Copyright (c) - +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. +--------------------------------------------------------- - +--------------------------------------------------------- - "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). +Microsoft.Bcl.AsyncInterfaces 7.0.0 - MIT - - "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To - +The MIT License (MIT) - "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." +Copyright (c) .NET Foundation and Contributors - +All rights reserved. - "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. - 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and +--------------------------------------------------------- - (b) You must cause any modified files to carry prominent notices stating that You changed the files; and +--------------------------------------------------------- - (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +Microsoft.CodeAnalysis.Common 4.4.0 - MIT - (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors - 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. +MIT License - 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. +Copyright (c) - 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -APPENDIX: How to apply the Apache License to your work. +--------------------------------------------------------- -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. +--------------------------------------------------------- -Copyright [yyyy] [name of copyright owner] +Microsoft.CodeAnalysis.CSharp 4.4.0 - MIT -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. +(c) Microsoft Corporation +Copyright (c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors +Copyright (c) Microsoft Corporation. Alle Rechte -You may obtain a copy of the License at +MIT License -http://www.apache.org/licenses/LICENSE-2.0 +Copyright (c) -Unless required by applicable law or agreed to in writing, software +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -distributed under the License is distributed on an "AS IS" BASIS, +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -See the License for the specific language governing permissions and +--------------------------------------------------------- -limitations under the License. +--------------------------------------------------------- -------------------------------------------------------------------- +Microsoft.CSharp 4.7.0 - MIT -------------------------------------------------------------------- -Microsoft.CodeAnalysis.CSharp 3.3.1 - Apache-2.0 -(c) 2008 VeriSign, Inc. (c) Microsoft Corporation. Copyright (c) .NET Foundation. -Copyright (c) Microsoft Corporation. -9Copyright (c) Microsoft Corporation. -ACopyright (c) Microsoft Corporation. -BCopyright (c) Microsoft Corporation. -CCopyright (c) Microsoft Corporation. -DCopyright (c) Microsoft Corporation. -OCopyright (c) Microsoft Corporation. -Copyright (c) Microsoft Corporation. Alle Rechte - -Apache License - -Version 2.0, January 2004 - -http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - - - "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - - - - "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. +Copyright (c) 2011, Google Inc. +(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Portions (c) International Organization +Copyright (c) 2015 The Chromium Authors. +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS - +The MIT License (MIT) - "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. +Copyright (c) .NET Foundation and Contributors - +All rights reserved. - "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. - - "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. +--------------------------------------------------------- - +--------------------------------------------------------- - "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). +Microsoft.Extensions.ObjectPool 7.0.3 - MIT - - "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - +MIT License - "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." +Copyright (c) - +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. +--------------------------------------------------------- - 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +--------------------------------------------------------- - (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and +Microsoft.Management.Infrastructure 2.0.0 - MIT - (b) You must cause any modified files to carry prominent notices stating that You changed the files; and - (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and +(c) Microsoft Corporation. - (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. +MIT License - You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. +Copyright (c) - 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. +--------------------------------------------------------- - 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS +--------------------------------------------------------- -APPENDIX: How to apply the Apache License to your work. +Microsoft.Management.Infrastructure.Runtime.Unix 2.0.0 - MIT -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright [yyyy] [name of copyright owner] +(c) Microsoft Corporation. -Licensed under the Apache License, Version 2.0 (the "License"); +MIT License -you may not use this file except in compliance with the License. +Copyright (c) -You may obtain a copy of the License at +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -http://www.apache.org/licenses/LICENSE-2.0 +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -Unless required by applicable law or agreed to in writing, software +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -distributed under the License is distributed on an "AS IS" BASIS, +--------------------------------------------------------- -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +--------------------------------------------------------- -See the License for the specific language governing permissions and +Microsoft.PowerShell.MarkdownRender 7.2.0 - MIT -limitations under the License. -------------------------------------------------------------------- +(c) Microsoft Corporation. +(c) Microsoft Corporation. PowerShell's Markdown Rendering project PowerShell Markdown Renderer -------------------------------------------------------------------- +MIT License -Markdig.Signed 0.17.1 - BSD-2-Clause -(c) 2008 VeriSign, Inc. +Copyright (c) -Copyright (c) . All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +--------------------------------------------------------- -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------- -------------------------------------------------------------------- +Microsoft.PowerShell.Native 7.3.2 - MIT -------------------------------------------------------------------- -Microsoft.ApplicationInsights 2.11.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. +(c) Microsoft Corporation +Copyright (c) by P.J. Plauger MIT License @@ -271,66 +321,13 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- - -------------------------------------------------------------------- - -Microsoft.NETCore.Platforms 3.0.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. -Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. -Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. -Copyright (c) 2013-2017, Alfred Klomp -Copyright (c) 2015-2017, Wojciech Mula -Copyright (c) 2005-2007, Nick Galbreath -Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. -Copyright (c) 2004-2006 Intel Corporation -Copyright (c) 2016-2017, Matthieu Darbois -Copyright (c) .NET Foundation Contributors -Copyright (c) .NET Foundation and Contributors -Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler -Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS - -The MIT License (MIT) - -Copyright (c) .NET Foundation and Contributors - -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +--------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +Microsoft.Security.Extensions 1.2.0 - MIT -------------------------------------------------------------------- -Microsoft.PowerShell.Native 7.0.0-preview.2 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) by P.J. Plauger MIT License @@ -342,32 +339,49 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Win32.Registry 5.0.0 - MIT -------------------------------------------------------------------- -Microsoft.Win32.Registry 4.6.0 - MIT -(c) 2008 VeriSign, Inc. (c) Microsoft Corporation. +Copyright (c) Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright 2012 the V8 project Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. +Copyright (c) 1998 Microsoft. To (c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2018 Alexander Chermyanin Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. +Copyright (c) The Internet Society 1997. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors +Copyright (c) The Internet Society (2003). Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -394,32 +408,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -Microsoft.Win32.Registry.AccessControl 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +Microsoft.Win32.Registry.AccessControl 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -446,32 +489,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -Microsoft.Win32.SystemEvents 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +Microsoft.Win32.SystemEvents 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -498,32 +570,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -Microsoft.Windows.Compatibility 3.0.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +Microsoft.Windows.Compatibility 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -550,12 +651,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- + +--------------------------------------------------------- + +Namotion.Reflection 2.1.1 - MIT -------------------------------------------------------------------- -Namotion.Reflection 1.0.7 - MIT -(c) 2008 VeriSign, Inc. MIT License @@ -567,15 +669,17 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- + +--------------------------------------------------------- + +Newtonsoft.Json 13.0.2 - MIT -------------------------------------------------------------------- -Newtonsoft.Json 12.0.2 - MIT -(c) 2008 VeriSign, Inc. Copyright James Newton-King 2008 Copyright (c) 2007 James Newton-King Copyright (c) James Newton-King 2008 +Copyright James Newton-King 2008 Json.NET The MIT License (MIT) @@ -599,15 +703,16 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- + +NJsonSchema 10.8.0 - MIT -NJsonSchema 10.0.27 - MIT -(c) 2008 VeriSign, Inc. -Copyright Rico Suter, 2018 -Copyright (c) Rico Suter, 2018 -Copyright Rico Suter, 2018 4JSON Schema + +Copyright Rico Suter, 2022 +Copyright (c) Rico Suter, 2022 +Copyright Rico Suter, 2022 4JSON Schema MIT License @@ -619,32 +724,61 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -runtime.linux-arm.runtime.native.System.IO.Ports 4.6.0-rc2.19462.14 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +runtime.linux-arm.runtime.native.System.IO.Ports 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -671,32 +805,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -runtime.linux-arm64.runtime.native.System.IO.Ports 4.6.0-rc2.19462.14 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +runtime.linux-arm64.runtime.native.System.IO.Ports 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -723,32 +886,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -runtime.linux-x64.runtime.native.System.IO.Ports 4.6.0-rc2.19462.14 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +runtime.linux-x64.runtime.native.System.IO.Ports 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -775,12 +967,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.native.System.Data.SqlClient.sni 4.7.0 - MIT -------------------------------------------------------------------- -runtime.native.System.Data.SqlClient.sni 4.6.0 - MIT -(c) 2008 VeriSign, Inc. (c) Microsoft Corporation. Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. @@ -827,32 +1020,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -runtime.native.System.IO.Ports 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +runtime.native.System.IO.Ports 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -879,32 +1101,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -runtime.osx-x64.runtime.native.System.IO.Ports 4.6.0-rc2.19462.14 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +runtime.osx-arm64.runtime.native.System.IO.Ports 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -931,32 +1182,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.CodeDom 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +runtime.osx-x64.runtime.native.System.IO.Ports 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -983,26 +1263,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.Collections.Immutable 1.5.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. +System.CodeDom 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1029,32 +1344,50 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Collections.Immutable 6.0.0 - MIT -------------------------------------------------------------------- -System.ComponentModel.Composition 4.6.0 - MIT -(c) 2008 VeriSign, Inc. (c) Microsoft Corporation. +Copyright (c) Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright 2012 the V8 project Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. +Copyright (c) 1998 Microsoft. To (c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2018 Alexander Chermyanin Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. +Copyright (c) The Internet Society 1997. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors +Copyright (c) The Internet Society (2003). Copyright (c) .NET Foundation and Contributors +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1081,32 +1414,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.ComponentModel.Composition.Registration 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.ComponentModel.Composition 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1133,32 +1495,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.Configuration.ConfigurationManager 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.ComponentModel.Composition.Registration 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1185,11 +1576,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Configuration.ConfigurationManager 7.0.0 - MIT -------------------------------------------------------------------- -System.Data.DataSetExtensions 4.5.0 - MIT +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1216,32 +1657,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.Data.Odbc 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.Data.Odbc 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1268,32 +1738,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.Data.OleDb 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.Data.OleDb 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1320,24 +1819,24 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.Data.SqlClient 4.7.0 - MIT -2008 SQL Server 2012 -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.Data.SqlClient 4.8.5 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +(c) 1997-2005 Sean Eron Anderson Copyright (c) 2007 James Newton-King Copyright (c) 1991-2017 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors @@ -1345,8 +1844,8 @@ Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers The MIT License (MIT) @@ -1373,32 +1872,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Diagnostics.DiagnosticSource 7.0.1 - MIT -------------------------------------------------------------------- -System.Diagnostics.EventLog 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. -Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. -Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. -Copyright (c) 2013-2017, Alfred Klomp -Copyright (c) 2015-2017, Wojciech Mula -Copyright (c) 2005-2007, Nick Galbreath -Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. -Copyright (c) 2004-2006 Intel Corporation -Copyright (c) 2016-2017, Matthieu Darbois -Copyright (c) .NET Foundation Contributors -Copyright (c) .NET Foundation and Contributors -Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler -Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS The MIT License (MIT) @@ -1425,32 +1905,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.Diagnostics.PerformanceCounter 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.Diagnostics.EventLog 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1477,32 +1986,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.DirectoryServices 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.Diagnostics.PerformanceCounter 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1529,32 +2067,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.DirectoryServices.AccountManagement 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.DirectoryServices 7.0.1 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1581,32 +2148,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.DirectoryServices.Protocols 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.DirectoryServices.AccountManagement 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1633,32 +2229,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.Drawing.Common 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.DirectoryServices.Protocols 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1685,32 +2310,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.IO.FileSystem.AccessControl 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.Drawing.Common 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1737,32 +2391,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.IO.Packaging 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.Formats.Asn1 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1789,26 +2472,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.IO.Pipes.AccessControl 4.5.1 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. +System.IO.Packaging 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1835,32 +2553,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.IO.Ports 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.IO.Ports 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1887,32 +2634,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.Management 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.Management 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -1939,25 +2715,28 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- + +System.Memory 4.5.5 - MIT -System.Memory 4.5.3 - MIT -(c) 2008 VeriSign, Inc. + +(c) 2022 GitHub, Inc. +(c) Microsoft Corporation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +(c) 1997-2005 Sean Eron Anderson Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2015 The Chromium Authors Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers The MIT License (MIT) @@ -1984,32 +2763,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.Net.Http.WinHttpHandler 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.Net.Http.WinHttpHandler 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -2036,60 +2844,20 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- - -------------------------------------------------------------------- - -System.Private.ServiceModel 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation and Contributors -Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) - -The MIT License (MIT) - -Copyright (c) .NET Foundation and Contributors - -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +--------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +System.Numerics.Vectors 4.5.0 - MIT -------------------------------------------------------------------- -System.Reflection.Context 4.6.0 - MIT -(c) 2008 VeriSign, Inc. (c) Microsoft Corporation. -Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. (c) 1997-2005 Sean Eron Anderson. -Copyright (c) 2007 James Newton-King Copyright (c) 1991-2017 Unicode, Inc. -Copyright (c) 2013-2017, Alfred Klomp -Copyright (c) 2015-2017, Wojciech Mula -Copyright (c) 2005-2007, Nick Galbreath Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation -Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) @@ -2123,11 +2891,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Private.ServiceModel 4.10.0 - MIT -------------------------------------------------------------------- -System.Reflection.DispatchProxy 4.5.0 - MIT The MIT License (MIT) @@ -2154,32 +2924,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.Reflection.Emit 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.Reflection.Context 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -2206,12 +3005,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Reflection.DispatchProxy 4.7.1 - MIT -------------------------------------------------------------------- -System.Reflection.Emit.ILGeneration 4.6.0 - MIT -(c) 2008 VeriSign, Inc. (c) Microsoft Corporation. Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. @@ -2258,32 +3058,49 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Reflection.Metadata 5.0.0 - MIT -------------------------------------------------------------------- -System.Reflection.Emit.Lightweight 4.6.0 - MIT -(c) 2008 VeriSign, Inc. (c) Microsoft Corporation. +Copyright (c) Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright 2012 the V8 project Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. +Copyright (c) 1998 Microsoft. To (c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2018 Alexander Chermyanin Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. +Copyright (c) The Internet Society 1997. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors +Copyright (c) The Internet Society (2003). Copyright (c) .NET Foundation and Contributors Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -2310,11 +3127,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- + +System.Runtime.Caching 7.0.0 - MIT -System.Reflection.Metadata 1.6.0 - MIT + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -2341,32 +3208,50 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Runtime.CompilerServices.Unsafe 6.0.0 - MIT -------------------------------------------------------------------- -System.Runtime.Caching 4.6.0 - MIT -(c) 2008 VeriSign, Inc. (c) Microsoft Corporation. +Copyright (c) Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright 2012 the V8 project Copyright (c) .NET Foundation. Copyright (c) 2011, Google Inc. +Copyright (c) 1998 Microsoft. To (c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet Copyright (c) 2013-2017, Alfred Klomp Copyright (c) 2015-2017, Wojciech Mula Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2018 Alexander Chermyanin Portions (c) International Organization Copyright (c) 2015 The Chromium Authors. +Copyright (c) The Internet Society 1997. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois Copyright (c) .NET Foundation Contributors +Copyright (c) The Internet Society (2003). Copyright (c) .NET Foundation and Contributors +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip. Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -2393,15 +3278,50 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Security.AccessControl 6.0.0 - MIT -------------------------------------------------------------------- -System.ServiceModel.Duplex 4.6.0 - MIT -(c) 2008 VeriSign, Inc. (c) Microsoft Corporation. +Copyright (c) Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright 2012 the V8 project +Copyright (c) .NET Foundation. +Copyright (c) 2011, Google Inc. +Copyright (c) 1998 Microsoft. To +(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2018 Alexander Chermyanin +Portions (c) International Organization +Copyright (c) 2015 The Chromium Authors. +Copyright (c) The Internet Society 1997. +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) .NET Foundation Contributors +Copyright (c) The Internet Society (2003). Copyright (c) .NET Foundation and Contributors -Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -2428,15 +3348,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.ServiceModel.Http 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. +System.Security.Cryptography.Pkcs 7.0.1 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors -Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -2463,15 +3429,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.ServiceModel.NetTcp 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. +System.Security.Cryptography.ProtectedData 7.0.1 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors -Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -2498,25 +3510,710 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- - -------------------------------------------------------------------- - -System.ServiceModel.Primitives 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation and Contributors -Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) +--------------------------------------------------------- -The MIT License (MIT) +--------------------------------------------------------- -Copyright (c) .NET Foundation and Contributors +System.Security.Cryptography.Xml 7.0.1 - MIT -All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Security.Permissions 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Security.Principal.Windows 5.0.0 - MIT + + +(c) Microsoft Corporation. +Copyright (c) Andrew Arnott +Copyright 2018 Daniel Lemire +Copyright 2012 the V8 project +Copyright (c) .NET Foundation. +Copyright (c) 2011, Google Inc. +Copyright (c) 1998 Microsoft. To +(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2018 Alexander Chermyanin +Portions (c) International Organization +Copyright (c) 2015 The Chromium Authors. +Copyright (c) The Internet Society 1997. +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) .NET Foundation Contributors +Copyright (c) The Internet Society (2003). +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ServiceModel.Duplex 4.10.0 - MIT + + + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ServiceModel.Http 4.10.0 - MIT + + + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ServiceModel.NetTcp 4.10.0 - MIT + + + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ServiceModel.Primitives 4.10.0 - MIT + + + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ServiceModel.Security 4.10.0 - MIT + + + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ServiceModel.Syndication 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ServiceProcess.ServiceController 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Speech 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Text.Encoding.CodePages 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: @@ -2533,15 +4230,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.ServiceModel.Security 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. +System.Text.Encodings.Web 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors -Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -2568,32 +4311,61 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.Threading.AccessControl 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.Threading.AccessControl 7.0.1 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -2620,12 +4392,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Threading.Tasks.Extensions 4.5.4 - MIT -------------------------------------------------------------------- -System.Threading.Tasks.Extensions 4.5.3 - MIT -(c) 2008 VeriSign, Inc. (c) Microsoft Corporation. Copyright (c) 2011, Google Inc. (c) 1997-2005 Sean Eron Anderson. @@ -2666,32 +4439,94 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------- +--------------------------------------------------------- -------------------------------------------------------------------- +--------------------------------------------------------- -System.Windows.Extensions 4.6.0 - MIT -(c) 2008 VeriSign, Inc. -(c) Microsoft Corporation. -Copyright (c) .NET Foundation. +System.Web.Services.Description 4.10.0 - MIT + + + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Windows.Extensions 7.0.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation Copyright (c) 2011, Google Inc. -(c) 1997-2005 Sean Eron Anderson. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1998 Microsoft. To +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2005-2020 Rich Felker +Copyright (c) Microsoft Corporation Copyright (c) 2007 James Newton-King -Copyright (c) 1991-2017 Unicode, Inc. +Copyright (c) 2012-2014, Yann Collet +Copyright (c) 1991-2022 Unicode, Inc. Copyright (c) 2013-2017, Alfred Klomp +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2021 csFastFloat authors Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 Portions (c) International Organization -Copyright (c) 2015 The Chromium Authors. Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) Copyright (c) .NET Foundation Contributors +Copyright (c) 2020 Mara Bos Copyright (c) .NET Foundation and Contributors +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen Copyright (c) 2011 Novell, Inc (http://www.novell.com) -Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) -Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. -Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass. To The MIT License (MIT) @@ -2718,17 +4553,148 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + + ------------------------------------------------------------------- ------------------------------------------------------------------- -Additional - +Additional - + +------------------------------------------------- +Microsoft.PowerShell.Archive +------------------------------------------------- + +Copyright (c) 2016 Microsoft Corporation. + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +------------------------------------------------- +Microsoft.Management.Infrastructure.Runtime.Unix +Microsoft.Management.Infrastructure +------------------------------------------------- + +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------- +• NuGet.Common +• NuGet.Configuration +• NuGet.DependencyResolver.Core +• NuGet.Frameworks +• NuGet.LibraryModel +• NuGet.Packaging +• NuGet.Packaging.Core +• NuGet.Packaging.Core.Types +• NuGet.ProjectModel +• NuGet.Protocol.Core.Types +• NuGet.Protocol.Core.v3 +• NuGet.Repositories +• NuGet.RuntimeModel +• NuGet.Versioning +---------------------------------------------------------- + +Copyright (c) .NET Foundation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +these files except in compliance with the License. You may obtain a copy of the +License at + +https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + +------------------------------------------------- +PackageManagement +------------------------------------------------- + +Copyright (c) Microsoft Corporation +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------- +PowerShellGet +------------------------------------------------- + +Copyright (c) Microsoft Corporation + +All rights reserved. + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. --------------------------------------------- File: PSReadLine --------------------------------------------- -https://github.com/lzybkr/PSReadLine +https://github.com/PowerShell/PSReadLine Copyright (c) 2013, Jason Shirk @@ -2756,35 +4722,16 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ----------------------------------------------- -File: Hashtables from ConvertFrom-json ----------------------------------------------- - -https://stackoverflow.com/questions/22002748/hashtables-from-convertfrom-json-have-different-type-from-powershells-built-in-h - -Copyright (c) 2015 Dave Wyatt. All rights reserved. - -All rights reserved. - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ------------------------------------------------- -PackageManagement +ThreadJob ------------------------------------------------- -Copyright (c) Microsoft Corporation -All rights reserved. +Copyright (c) 2018 Paul Higinbotham MIT License Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the Software), to deal +of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is @@ -2793,7 +4740,7 @@ furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER @@ -2801,34 +4748,3 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -• NuGet.Common -• NuGet.Configuration -• NuGet.DependencyResolver.Core -• NuGet.Frameworks -• NuGet.LibraryModel -• NuGet.Packaging -• NuGet.Packaging.Core -• NuGet.Packaging.Core.Types -• NuGet.ProjectModel -• NuGet.Protocol.Core.Types -• NuGet.Protocol.Core.v3 -• NuGet.Repositories -• NuGet.RuntimeModel -• NuGet.Versioning ----------------------------------------------------------- - -Copyright (c) .NET Foundation. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -these files except in compliance with the License. You may obtain a copy of the -License at - -https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. - -------------------------------------------------------------------- diff --git a/assets/AppxManifest.xml b/assets/AppxManifest.xml index bba68ca976b..c646bcdf94b 100644 --- a/assets/AppxManifest.xml +++ b/assets/AppxManifest.xml @@ -45,5 +45,7 @@ + + diff --git a/assets/MicrosoftUpdate/RegisterMicrosoftUpdate.ps1 b/assets/MicrosoftUpdate/RegisterMicrosoftUpdate.ps1 new file mode 100644 index 00000000000..fd341d4fbef --- /dev/null +++ b/assets/MicrosoftUpdate/RegisterMicrosoftUpdate.ps1 @@ -0,0 +1,70 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +param( + [ValidateSet('Hang', 'Fail')] + $TestHook +) + +$waitTimeoutSeconds = 300 +switch ($TestHook) { + 'Hang' { + $waitTimeoutSeconds = 10 + $jobScript = { Start-Sleep -Seconds 600 } + } + 'Fail' { + $jobScript = { throw "This job script should fail" } + } + default { + $jobScript = { + # This registers Microsoft Update via a predifened GUID with the Windows Update Agent. + # https://docs.microsoft.com/en-us/windows/win32/wua_sdk/opt-in-to-microsoft-update + + $serviceManager = (New-Object -ComObject Microsoft.Update.ServiceManager) + $isRegistered = $serviceManager.QueryServiceRegistration('7971f918-a847-4430-9279-4a52d1efe18d').Service.IsRegisteredWithAu + + if (!$isRegistered) { + Write-Verbose -Verbose "Opting into Microsoft Update as the Autmatic Update Service" + # 7 is the combination of asfAllowPendingRegistration, asfAllowOnlineRegistration, asfRegisterServiceWithAU + # AU means Automatic Updates + $null = $serviceManager.AddService2('7971f918-a847-4430-9279-4a52d1efe18d', 7, '') + } + else { + Write-Verbose -Verbose "Microsoft Update is already registered for Automatic Updates" + } + + $isRegistered = $serviceManager.QueryServiceRegistration('7971f918-a847-4430-9279-4a52d1efe18d').Service.IsRegisteredWithAu + + # Return if it was successful, which is the opposite of Pending. + return $isRegistered + } + } +} + +Write-Verbose "Running job script: $jobScript" -Verbose +$job = Start-ThreadJob -ScriptBlock $jobScript + +Write-Verbose "Waiting on Job for $waitTimeoutSeconds seconds" -Verbose +$null = Wait-Job -Job $job -Timeout $waitTimeoutSeconds + +if ($job.State -ne 'Running') { + Write-Verbose "Job finished. State: $($job.State)" -Verbose + $result = Receive-Job -Job $job -Verbose + Write-Verbose "Result: $result" -Verbose + if ($result) { + Write-Verbose "Registration succeeded" -Verbose + exit 0 + } + else { + Write-Verbose "Registration failed" -Verbose + # at the time this was written, the MSI is ignoring the exit code + exit 1 + } +} +else { + Write-Verbose "Job timed out" -Verbose + Write-Verbose "Stopping Job. State: $($job.State)" -Verbose + Stop-Job -Job $job + # at the time this was written, the MSI is ignoring the exit code + exit 258 +} diff --git a/assets/additionalAttributions.txt b/assets/additionalAttributions.txt index d244bad6877..6676ca99cf5 100644 --- a/assets/additionalAttributions.txt +++ b/assets/additionalAttributions.txt @@ -1,46 +1,42 @@ -## Used to generate a new TPN -## Copy this into the additional attributions fields -## Copy everything below here, but do not include this line ---------------------------------------------- -File: PSReadLine ---------------------------------------------- +------------------------------------------------------------------- -https://github.com/lzybkr/PSReadLine +------------------------------------------------------------------- -Copyright (c) 2013, Jason Shirk +Additional - -All rights reserved. +------------------------------------------------- +Microsoft.PowerShell.Archive +------------------------------------------------- -BSD License +Copyright (c) 2016 Microsoft Corporation. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +The MIT License (MIT) -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. ----------------------------------------------- -File: Hashtables from ConvertFrom-json ----------------------------------------------- +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. -https://stackoverflow.com/questions/22002748/hashtables-from-convertfrom-json-have-different-type-from-powershells-built-in-h +------------------------------------------------- +Microsoft.Management.Infrastructure.Runtime.Unix +Microsoft.Management.Infrastructure +------------------------------------------------- -Copyright (c) 2015 Dave Wyatt. All rights reserved. +Copyright (c) Microsoft Corporation All rights reserved. @@ -52,6 +48,36 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------- +• NuGet.Common +• NuGet.Configuration +• NuGet.DependencyResolver.Core +• NuGet.Frameworks +• NuGet.LibraryModel +• NuGet.Packaging +• NuGet.Packaging.Core +• NuGet.Packaging.Core.Types +• NuGet.ProjectModel +• NuGet.Protocol.Core.Types +• NuGet.Protocol.Core.v3 +• NuGet.Repositories +• NuGet.RuntimeModel +• NuGet.Versioning +---------------------------------------------------------- + +Copyright (c) .NET Foundation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +these files except in compliance with the License. You may obtain a copy of the +License at + +https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + ------------------------------------------------- PackageManagement ------------------------------------------------- @@ -79,32 +105,88 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -• NuGet.Common -• NuGet.Configuration -• NuGet.DependencyResolver.Core -• NuGet.Frameworks -• NuGet.LibraryModel -• NuGet.Packaging -• NuGet.Packaging.Core -• NuGet.Packaging.Core.Types -• NuGet.ProjectModel -• NuGet.Protocol.Core.Types -• NuGet.Protocol.Core.v3 -• NuGet.Repositories -• NuGet.RuntimeModel -• NuGet.Versioning ----------------------------------------------------------- +------------------------------------------------- +PowerShellGet +------------------------------------------------- -Copyright (c) .NET Foundation. All rights reserved. +Copyright (c) Microsoft Corporation -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -these files except in compliance with the License. You may obtain a copy of the -License at +All rights reserved. -https://www.apache.org/licenses/LICENSE-2.0 +The MIT License (MIT) -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--------------------------------------------- +File: PSReadLine +--------------------------------------------- + +https://github.com/PowerShell/PSReadLine + +Copyright (c) 2013, Jason Shirk + +All rights reserved. + +BSD License + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------- +ThreadJob +------------------------------------------------- + +Copyright (c) 2018 Paul Higinbotham + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/assets/wix/Product.wxs b/assets/wix/Product.wxs index da25dee7b04..8dc2d3a3f16 100644 --- a/assets/wix/Product.wxs +++ b/assets/wix/Product.wxs @@ -87,7 +87,7 @@ + Value=""[VersionFolder]pwsh.exe" -NoProfile -ExecutionPolicy Bypass -File "[VersionFolder]RegisterMicrosoftUpdate.ps1"" /> - + @@ -350,7 +350,7 @@ - + @@ -361,7 +361,7 @@ See the Microsoft Update FAQ]]> - Read the Microsoft Update Priacy Statement]]> + Read the Microsoft Update Privacy Statement]]> diff --git a/assets/wix/files.wxs b/assets/wix/files.wxs index 8e821f1c6fc..2a0bfe6afb7 100644 --- a/assets/wix/files.wxs +++ b/assets/wix/files.wxs @@ -71,24 +71,15 @@ - - - - - - - - - @@ -101,9 +92,6 @@ - - - @@ -134,39 +122,18 @@ - - - - - - - - - - - - - - - - - - - - - @@ -182,18 +149,12 @@ - - - - - - @@ -203,15 +164,9 @@ - - - - - - @@ -221,12 +176,6 @@ - - - - - - @@ -236,9 +185,6 @@ - - - @@ -269,27 +215,12 @@ - - - - - - - - - - - - - - - @@ -308,12 +239,6 @@ - - - - - - @@ -329,48 +254,24 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -383,33 +284,18 @@ - - - - - - - - - - - - - - - @@ -422,15 +308,9 @@ - - - - - - @@ -449,24 +329,12 @@ - - - - - - - - - - - - @@ -488,27 +356,12 @@ - - - - - - - - - - - - - - - @@ -518,12 +371,6 @@ - - - - - - @@ -536,45 +383,21 @@ - - - - - - - - - - - - - - - - - - - - - - - - @@ -590,9 +413,6 @@ - - - @@ -611,45 +431,24 @@ - - - - - - - - - - - - - - - - - - - - - @@ -668,24 +467,15 @@ - - - - - - - - - @@ -698,57 +488,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -761,78 +521,36 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -848,54 +566,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -905,21 +596,12 @@ - - - - - - - - - @@ -929,15 +611,9 @@ - - - - - - @@ -959,42 +635,21 @@ - - - - - - - - - - - - - - - - - - - - - @@ -1004,78 +659,33 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1097,66 +707,36 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1605,18 +1185,9 @@ - - - - - - - - - @@ -1638,6 +1209,27 @@ + + + + + + + + + + + + + + + + + + + + + @@ -1708,6 +1300,9 @@ + + + @@ -1723,9 +1318,6 @@ - - - @@ -1740,9 +1332,6 @@ - - - @@ -1832,9 +1421,29 @@ - - + + + + + + + + + + + + + + + + + + + + + + @@ -1987,6 +1596,12 @@ + + + + + + @@ -2046,6 +1661,12 @@ + + + + + + @@ -2105,6 +1726,12 @@ + + + + + + @@ -2164,6 +1791,12 @@ + + + + + + @@ -2223,6 +1856,12 @@ + + + + + + @@ -2282,6 +1921,12 @@ + + + + + + @@ -2341,6 +1986,12 @@ + + + + + + @@ -2400,6 +2051,12 @@ + + + + + + @@ -2459,6 +2116,12 @@ + + + + + + @@ -2518,6 +2181,12 @@ + + + + + + @@ -2577,6 +2246,12 @@ + + + + + + @@ -2636,6 +2311,12 @@ + + + + + + @@ -2695,6 +2376,12 @@ + + + + + + @@ -2880,9 +2567,6 @@ - - - @@ -2967,9 +2651,6 @@ - - - @@ -3033,12 +2714,6 @@ - - - - - - @@ -3054,8 +2729,57 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -3084,17 +2808,13 @@ - - - - @@ -3105,41 +2825,27 @@ - - - - - - - - - - - - - - @@ -3150,72 +2856,46 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3223,156 +2903,92 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3380,45 +2996,23 @@ - - - - - - - - - - - - - - - - - - - - - - @@ -3426,26 +3020,16 @@ - - - - - - - - - - @@ -3595,6 +3179,21 @@ + + + + + + + + + + + + + + + @@ -3608,15 +3207,14 @@ + - - @@ -3642,7 +3240,11 @@ - + + + + + @@ -3684,6 +3286,8 @@ + + @@ -3703,6 +3307,8 @@ + + @@ -3722,6 +3328,8 @@ + + @@ -3741,6 +3349,8 @@ + + @@ -3760,6 +3370,8 @@ + + @@ -3779,6 +3391,8 @@ + + @@ -3798,6 +3412,8 @@ + + @@ -3817,6 +3433,8 @@ + + @@ -3836,6 +3454,8 @@ + + @@ -3855,6 +3475,8 @@ + + @@ -3874,6 +3496,8 @@ + + @@ -3893,6 +3517,8 @@ + + @@ -3912,6 +3538,8 @@ + + @@ -3972,7 +3600,6 @@ - @@ -4001,7 +3628,6 @@ - @@ -4022,26 +3648,28 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/build.psm1 b/build.psm1 index bbb98110d2a..115897f71ce 100644 --- a/build.psm1 +++ b/build.psm1 @@ -6,6 +6,9 @@ param( [parameter(Mandatory = $false)][switch]$SkipLinuxDistroCheck = $false ) +. "$PSScriptRoot\tools\buildCommon\startNativeExecution.ps1" + +# CI runs with PowerShell 5.0, so don't use features like ?: && || Set-StrictMode -Version 3.0 # On Unix paths is separated by colon @@ -16,6 +19,9 @@ $script:Options = $null $dotnetMetadata = Get-Content $PSScriptRoot/DotnetRuntimeMetadata.json | ConvertFrom-Json $dotnetCLIChannel = $dotnetMetadata.Sdk.Channel $dotnetCLIQuality = $dotnetMetadata.Sdk.Quality +$dotnetAzureFeed = $env:__DONET_RUNTIME_FEED ?? $dotnetMetadata.Sdk.azureFeed +$dotnetAzureFeedSecret = $env:__DONET_RUNTIME_FEED_KEY +$dotnetSDKVersionOveride = $dotnetMetadata.Sdk.sdkImageOverride $dotnetCLIRequiredVersion = $(Get-Content $PSScriptRoot/global.json | ConvertFrom-Json).Sdk.Version # Track if tags have been sync'ed @@ -265,6 +271,8 @@ function Test-IsReleaseCandidate return $false } +$optimizedFddRegex = 'fxdependent-(linux|alpine|win|win7|osx)-(x64|x86|arm64|arm)' + function Start-PSBuild { [CmdletBinding(DefaultParameterSetName="Default")] param( @@ -299,6 +307,7 @@ function Start-PSBuild { # If this parameter is not provided it will get determined automatically. [ValidateSet("alpine-x64", "fxdependent", + "fxdependent-linux-x64", "fxdependent-win-desktop", "linux-arm", "linux-arm64", @@ -311,17 +320,16 @@ function Start-PSBuild { "win7-x86")] [string]$Runtime, - [ValidateSet('Debug', 'Release', 'CodeCoverage', '')] # We might need "Checked" as well + [ValidateSet('Debug', 'Release', 'CodeCoverage', 'StaticAnalysis', '')] # We might need "Checked" as well [string]$Configuration, - [switch]$CrossGen, - [ValidatePattern("^v\d+\.\d+\.\d+(-\w+(\.\d{1,2})?)?$")] [ValidateNotNullOrEmpty()] [string]$ReleaseTag, [switch]$Detailed, [switch]$InteractiveAuth, - [switch]$SkipRoslynAnalyzers + [switch]$SkipRoslynAnalyzers, + [string]$PSOptionsPath ) if ($ReleaseTag -and $ReleaseTag -notmatch "^v\d+\.\d+\.\d+(-(preview|rc)(\.\d{1,2})?)?$") { @@ -342,10 +350,6 @@ function Start-PSBuild { } if ($ForMinimalSize) { - if ($CrossGen) { - throw "Build for the minimal size requires the minimal disk footprint, so `CrossGen` is not allowed" - } - if ($Runtime -and "linux-x64", "win7-x64", "osx-x64" -notcontains $Runtime) { throw "Build for the minimal size is enabled only for following runtimes: 'linux-x64', 'win7-x64', 'osx-x64'" } @@ -391,7 +395,8 @@ function Start-PSBuild { } # Verify if the dotnet in-use is the required version - $dotnetCLIInstalledVersion = Start-NativeExecution -sb { dotnet --version } -IgnoreExitcode + $dotnetCLIInstalledVersion = Find-RequiredSDK $dotnetCLIRequiredVersion + If ($dotnetCLIInstalledVersion -ne $dotnetCLIRequiredVersion) { Write-Warning @" The currently installed .NET Command Line Tools is not the required version. @@ -413,7 +418,6 @@ Fix steps: # set output options $OptionsArguments = @{ - CrossGen=$CrossGen Output=$Output Runtime=$Runtime Configuration=$Configuration @@ -440,6 +444,9 @@ Fix steps: # Add --self-contained due to "warning NETSDK1179: One of '--self-contained' or '--no-self-contained' options are required when '--runtime' is used." if ($Options.Runtime -like 'fxdependent*') { $Arguments += "--no-self-contained" + # The UseAppHost = true property creates ".exe" for the fxdependent packages. + # We need this in the package as Start-Job needs it. + $Arguments += "/property:UseAppHost=true" } else { $Arguments += "--self-contained" @@ -455,7 +462,7 @@ Fix steps: # Framework Dependent builds do not support ReadyToRun as it needs a specific runtime to optimize for. # The property is set in Powershell.Common.props file. # We override the property through the build command line. - if($Options.Runtime -like 'fxdependent*' -or $ForMinimalSize) { + if(($Options.Runtime -like 'fxdependent*' -or $ForMinimalSize) -and $Options.Runtime -notmatch $optimizedFddRegex) { $Arguments += "/property:PublishReadyToRun=false" } @@ -470,6 +477,9 @@ Fix steps: if (-not $SMAOnly -and $Options.Runtime -notlike 'fxdependent*') { # libraries should not have runtime $Arguments += "--runtime", $Options.Runtime + } elseif ($Options.Runtime -match $optimizedFddRegex) { + $runtime = $Options.Runtime -replace 'fxdependent-', '' + $Arguments += "--runtime", $runtime } if ($ReleaseTag) { @@ -493,7 +503,12 @@ Fix steps: # Handle TypeGen # .inc file name must be different for Windows and Linux to allow build on Windows and WSL. - $incFileName = "powershell_$($Options.Runtime).inc" + $runtime = $Options.Runtime + if ($Options.Runtime -match $optimizedFddRegex) { + $runtime = $Options.Runtime -replace 'fxdependent-', '' + } + + $incFileName = "powershell_$runtime.inc" if ($TypeGen -or -not (Test-Path "$PSScriptRoot/src/TypeCatalogGen/$incFileName")) { Write-Log -message "Run TypeGen (generating CorePsTypeCatalog.cs)" Start-TypeGen -IncFileName $incFileName @@ -511,7 +526,8 @@ Fix steps: # Relative paths do not work well if cwd is not changed to project Push-Location $Options.Top - if ($Options.Runtime -notlike 'fxdependent*') { + if ($Options.Runtime -notlike 'fxdependent*' -or $Options.Runtime -match $optimizedFddRegex) { + Write-Verbose "Building without shim" -Verbose $sdkToUse = 'Microsoft.NET.Sdk' if ($Options.Runtime -like 'win7-*' -and !$ForMinimalSize) { ## WPF/WinForm and the PowerShell GraphicalHost assemblies are included @@ -524,13 +540,8 @@ Fix steps: Write-Log -message "Run dotnet $Arguments from $PWD" Start-NativeExecution { dotnet $Arguments } Write-Log -message "PowerShell output: $($Options.Output)" - - if ($CrossGen) { - # fxdependent package cannot be CrossGen'ed - Start-CrossGen -PublishPath $publishPath -Runtime $script:Options.Runtime - Write-Log -message "pwsh.exe with ngen binaries is available at: $($Options.Output)" - } } else { + Write-Verbose "Building with shim" -Verbose $globalToolSrcFolder = Resolve-Path (Join-Path $Options.Top "../Microsoft.PowerShell.GlobalTool.Shim") | Select-Object -ExpandProperty Path if ($Options.Runtime -eq 'fxdependent') { @@ -545,7 +556,9 @@ Fix steps: try { Push-Location $globalToolSrcFolder - $Arguments += "--output", $publishPath + if ($Arguments -notcontains '--output') { + $Arguments += "--output", $publishPath + } Write-Log -message "Run dotnet $Arguments from $PWD to build global tool entry point" Start-NativeExecution { dotnet $Arguments } } @@ -623,40 +636,33 @@ Fix steps: # publish powershell.config.json $config = @{} - if ($environment.IsWindows) { + + if ($Options.Runtime -like "*win*") { + # Execution Policy is only supported on Windows $config = @{ "Microsoft.PowerShell:ExecutionPolicy" = "RemoteSigned"; - "WindowsPowerShellCompatibilityModuleDenyList" = @("PSScheduledJob","BestPractices","UpdateServices") } + "WindowsPowerShellCompatibilityModuleDenyList" = @("PSScheduledJob", "BestPractices", "UpdateServices") + } } - # When building preview, we want the configuration to enable all experiemental features by default - # ARM is cross compiled, so we can't run pwsh to enumerate Experimental Features if (-not $SkipExperimentalFeatureGeneration -and (Test-IsPreview $psVersion) -and - -not (Test-IsReleaseCandidate $psVersion) -and - -not $Runtime.Contains("arm") -and - -not ($Runtime -like 'fxdependent*')) { - - $json = & $publishPath\pwsh -noprofile -command { - # Special case for DSC code in PS; - # this experimental feature requires new DSC module that is not inbox, - # so we don't want default DSC use case be broken - [System.Collections.ArrayList] $expFeatures = Get-ExperimentalFeature | Where-Object Name -NE PS7DscSupport | ForEach-Object -MemberName Name - - $expFeatures | Out-String | Write-Verbose -Verbose - - # Make sure ExperimentalFeatures from modules in PSHome are added - # https://github.com/PowerShell/PowerShell/issues/10550 - $ExperimentalFeaturesFromGalleryModulesInPSHome = @() - $ExperimentalFeaturesFromGalleryModulesInPSHome | ForEach-Object { - if (!$expFeatures.Contains($_)) { - $null = $expFeatures.Add($_) - } - } + -not (Test-IsReleaseCandidate $psVersion) + ) { + + $ExperimentalFeatureJsonFilePath = if ($Options.Runtime -like "*win*") { + "$PSScriptRoot/experimental-feature-windows.json" + } else { + "$PSScriptRoot/experimental-feature-linux.json" + } - ConvertTo-Json $expFeatures + if (-not (Test-Path $ExperimentalFeatureJsonFilePath)) { + throw "ExperimentalFeatureJsonFilePath: $ExperimentalFeatureJsonFilePath does not exist" } + $json = Get-Content -Raw $ExperimentalFeatureJsonFilePath $config += @{ ExperimentalFeatures = ([string[]] ($json | ConvertFrom-Json)) } + } else { + Write-Warning -Message "Experimental features are not enabled in powershell.config.json file" } if ($config.Count -gt 0) { @@ -668,6 +674,17 @@ Fix steps: if ($CI) { Restore-PSPester -Destination (Join-Path $publishPath "Modules") } + + Clear-NativeDependencies -PublishFolder $publishPath + + if ($PSOptionsPath) { + $resolvedPSOptionsPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($PSOptionsPath) + $parent = Split-Path -Path $resolvedPSOptionsPath + if (!(Test-Path $parent)) { + $null = New-Item -ItemType Directory -Path $parent + } + Save-PSOptions -PSOptionsPath $PSOptionsPath -Options $Options + } } function Restore-PSPackage @@ -800,17 +817,18 @@ function Compress-TestContent { function New-PSOptions { [CmdletBinding()] param( - [ValidateSet("Debug", "Release", "CodeCoverage", '')] + [ValidateSet('Debug', 'Release', 'CodeCoverage', 'StaticAnalysis', '')] [string]$Configuration, - [ValidateSet("net6.0")] - [string]$Framework = "net6.0", + [ValidateSet("net7.0")] + [string]$Framework = "net7.0", # These are duplicated from Start-PSBuild # We do not use ValidateScript since we want tab completion [ValidateSet("", "alpine-x64", "fxdependent", + "fxdependent-linux-x64", "fxdependent-win-desktop", "linux-arm", "linux-arm64", @@ -823,8 +841,6 @@ function New-PSOptions { "win7-x86")] [string]$Runtime, - [switch]$CrossGen, - # Accept a path to the output directory # If not null or empty, name of the executable will be appended to # this path, otherwise, to the default path, and then the full path @@ -849,26 +865,26 @@ function New-PSOptions { Write-Verbose "Using framework '$Framework'" if (-not $Runtime) { - if ($environment.IsLinux) { - $Runtime = "linux-x64" - } elseif ($environment.IsMacOS) { - if ($PSVersionTable.OS.Contains('ARM64')) { - $Runtime = "osx-arm64" - } - else { - $Runtime = "osx-x64" + $Platform, $Architecture = dotnet --info | + Select-String '^\s*OS Platform:\s+(\w+)$', '^\s*Architecture:\s+(\w+)$' | + Select-Object -First 2 | + ForEach-Object { $_.Matches.Groups[1].Value } + + switch ($Platform) { + 'Windows' { + # For x86 and x64 architectures, we use win7-x64 and win7-x86 RIDs. + # For arm and arm64 architectures, we use win-arm and win-arm64 RIDs. + $Platform = if ($Architecture[0] -eq 'x') { 'win7' } else { 'win' } + $Runtime = "${Platform}-${Architecture}" } - } else { - $RID = dotnet --info | ForEach-Object { - if ($_ -match "RID") { - $_ -split "\s+" | Select-Object -Last 1 - } + + 'Linux' { + $Runtime = "linux-${Architecture}" } - # We plan to release packages targeting win7-x64 and win7-x86 RIDs, - # which supports all supported windows platforms. - # So we, will change the RID to win7- - $Runtime = $RID -replace "win\d+", "win7" + 'Darwin' { + $Runtime = "osx-${Architecture}" + } } if (-not $Runtime) { @@ -930,7 +946,6 @@ function New-PSOptions { -RootInfo ([PSCustomObject]$RootInfo) ` -Top $Top ` -Runtime $Runtime ` - -Crossgen $Crossgen.IsPresent ` -Configuration $Configuration ` -PSModuleRestore $PSModuleRestore.IsPresent ` -Framework $Framework ` @@ -1039,6 +1054,48 @@ function Get-PesterTag { $o } +# Function to build and publish the Microsoft.PowerShell.NamedPipeConnection module for +# testing PowerShell remote custom connections. +function Publish-CustomConnectionTestModule +{ + $sourcePath = "${PSScriptRoot}/test/tools/NamedPipeConnection" + $outPath = "${PSScriptRoot}/test/tools/NamedPipeConnection/out/Microsoft.PowerShell.NamedPipeConnection" + $publishPath = "${PSScriptRoot}/test/tools/Modules" + $refPath = "${sourcePath}/src/code/Ref" + + # Copy the current SMA build to the refPath. + $smaPath = Join-Path -Path (Split-Path -Path (Get-PSOutput)) -ChildPath 'System.Management.Automation.dll' + if (! (Test-Path -Path $smaPath)) { + throw "Publish-CustomConnectionTestModule: Cannot find reference SMA at: ${smaPath}" + } + if (! (Test-Path -Path $refPath)) { + $null = New-Item -Path $refPath -ItemType Directory -Force + } + Copy-Item -Path $smapath -Destination $refPath -Force + + Find-DotNet + + Push-Location -Path $sourcePath + try { + # Build the Microsoft.PowerShell.NamedPipeConnect module + ./build.ps1 -Clean -Build + + if (! (Test-Path -Path $outPath)) { + throw "Publish-CustomConnectionTestModule: Build failed. Output path does not exist: $outPath" + } + + # Publish the Microsoft.PowerShell.NamedPipeConnection module + Copy-Item -Path $outPath -Destination $publishPath -Recurse -Force + + # Clean up build artifacts + ./build.ps1 -Clean + Remove-Item -Path $refPath -Recurse -Force -ErrorAction SilentlyContinue + } + finally { + Pop-Location + } +} + function Publish-PSTestTools { [CmdletBinding()] param( @@ -1049,9 +1106,10 @@ function Publish-PSTestTools { Find-Dotnet $tools = @( - @{Path="${PSScriptRoot}/test/tools/TestExe";Output="testexe"} - @{Path="${PSScriptRoot}/test/tools/WebListener";Output="WebListener"} - @{Path="${PSScriptRoot}/test/tools/TestService";Output="TestService"} + @{ Path="${PSScriptRoot}/test/tools/TestAlc"; Output="library" } + @{ Path="${PSScriptRoot}/test/tools/TestExe"; Output="exe" } + @{ Path="${PSScriptRoot}/test/tools/WebListener"; Output="exe" } + @{ Path="${PSScriptRoot}/test/tools/TestService"; Output="exe" } ) $Options = Get-PSOptions -DefaultToNew @@ -1072,10 +1130,26 @@ function Publish-PSTestTools { Remove-Item -Path $objPath -Recurse -Force } + if ($tool.Output -eq 'library') { + ## Handle building and publishing assemblies. + dotnet publish --configuration $Options.Configuration --framework $Options.Framework + continue + } + + ## Handle building and publishing executables. if (-not $runtime) { - dotnet publish --output bin --configuration $Options.Configuration --framework $Options.Framework --runtime $Options.Runtime - } else { - dotnet publish --output bin --configuration $Options.Configuration --framework $Options.Framework --runtime $runtime + $runtime = $Options.Runtime + } + + Write-Verbose -Verbose -Message "Starting dotnet publish for $toolPath with runtime $runtime" + + dotnet publish --output bin --configuration $Options.Configuration --framework $Options.Framework --runtime $runtime --self-contained | Out-String | Write-Verbose -Verbose + + $dll = $null + $dll = Get-ChildItem -Path bin -Recurse -Filter "*.dll" + + if (-not $dll) { + throw "Failed to find exe in $toolPath" } if ( -not $env:PATH.Contains($toolPath) ) { @@ -1106,6 +1180,7 @@ function Start-PSPester { [CmdletBinding(DefaultParameterSetName='default')] param( [Parameter(Position=0)] + [ArgumentCompleter({param($c,$p,$word) Get-ChildItem -Recurse -File -LiteralPath $PSScriptRoot/Test/PowerShell -filter *.tests.ps1 | Where-Object FullName -like "*$word*" })] [string[]]$Path = @("$PSScriptRoot/test/powershell"), [string]$OutputFormat = "NUnitXml", [string]$OutputFile = "pester-tests.xml", @@ -1199,6 +1274,9 @@ function Start-PSPester { $publishArgs['runtime'] = 'alpine-x64' } Publish-PSTestTools @publishArgs | ForEach-Object {Write-Host $_} + + # Publish the Microsoft.PowerShell.NamedPipeConnection module for testing custom remote connections. + Publish-CustomConnectionTestModule | ForEach-Object { Write-Host $_ } } # All concatenated commands/arguments are suffixed with the delimiter (space) @@ -1496,7 +1574,7 @@ function Publish-TestResults # If we attempt to upload a result file which has no test cases in it, then vsts will produce a warning # so check to be sure we actually have a result file that contains test cases to upload. - # If the the "test-case" count is greater than 0, then we have results. + # If the "test-case" count is greater than 0, then we have results. # Regardless, we want to upload this as an artifact, so this logic doesn't pertain to that. if ( @(([xml](Get-Content $Path)).SelectNodes(".//test-case")).Count -gt 0 -or $Type -eq 'XUnit' ) { Write-Host "##vso[results.publish type=$Type;mergeResults=true;runTitle=$Title;publishRunAttachments=true;resultFiles=$tempFilePath;failTaskOnFailedTests=true]" @@ -1536,14 +1614,14 @@ function Show-PSPesterError $description = $testFailure.description $name = $testFailure.name $message = $testFailure.failure.message - $StackTrace = $testFailure.failure."stack-trace" + $stack_trace = $testFailure.failure."stack-trace" } elseif ($PSCmdlet.ParameterSetName -eq 'object') { $description = $testFailureObject.Describe + '/' + $testFailureObject.Context $name = $testFailureObject.Name $message = $testFailureObject.FailureMessage - $StackTrace = $testFailureObject.StackTrace + $stack_trace = $testFailureObject.StackTrace } else { @@ -1555,7 +1633,7 @@ function Show-PSPesterError Write-Log -isError -message "message:" Write-Log -isError -message $message Write-Log -isError -message "stack-trace:" - Write-Log -isError -message $StackTrace + Write-Log -isError -message $stack_trace } @@ -1593,14 +1671,14 @@ function Test-XUnitTestResults $description = $failure.type $name = $failure.method $message = $failure.failure.message - $StackTrace = $failure.failure.'stack-trace' + $stack_trace = $failure.failure.'stack-trace' Write-Log -isError -message ("Description: " + $description) Write-Log -isError -message ("Name: " + $name) Write-Log -isError -message "message:" Write-Log -isError -message $message Write-Log -isError -message "stack-trace:" - Write-Log -isError -message $StackTrace + Write-Log -isError -message $stack_trace Write-Log -isError -message " " } @@ -1656,9 +1734,18 @@ function Test-PSPesterResults } elseif ($PSCmdlet.ParameterSetName -eq 'PesterPassThruObject') { - if ($ResultObject.TotalCount -le 0 -and -not $CanHaveNoResult) + if (-not $CanHaveNoResult) { - throw 'NO TESTS RUN' + $noTotalCountMember = if ($null -eq (Get-Member -InputObject $ResultObject -Name 'TotalCount')) { $true } else { $false } + if ($noTotalCountMember) + { + Write-Verbose -Verbose -Message "`$ResultObject has no 'TotalCount' property" + Write-Verbose -Verbose "$($ResultObject | Out-String)" + } + if ($noTotalCountMember -or $ResultObject.TotalCount -le 0) + { + throw 'NO TESTS RUN' + } } elseif ($ResultObject.FailedCount -gt 0) { @@ -1686,9 +1773,15 @@ function Start-PSxUnit { throw "PowerShell must be built before running tests!" } + $originalDOTNET_ROOT = $env:DOTNET_ROOT + try { Push-Location $PSScriptRoot/test/xUnit + # Add workaround to unblock xUnit testing see issue: https://github.com/dotnet/sdk/issues/26462 + $dotnetPath = if ($environment.IsWindows) { "$env:LocalAppData\Microsoft\dotnet" } else { "$env:HOME/.dotnet" } + $env:DOTNET_ROOT = $dotnetPath + # Path manipulation to obtain test project output directory if(-not $environment.IsWindows) @@ -1732,6 +1825,7 @@ function Start-PSxUnit { Publish-TestResults -Path $xUnitTestResultsFile -Type 'XUnit' -Title 'Xunit Sequential' } finally { + $env:DOTNET_ROOT = $originalDOTNET_ROOT Pop-Location } } @@ -1742,43 +1836,51 @@ function Install-Dotnet { [string]$Channel = $dotnetCLIChannel, [string]$Version = $dotnetCLIRequiredVersion, [string]$Quality = $dotnetCLIQuality, + [switch]$RemovePreviousVersion, [switch]$NoSudo, [string]$InstallDir, [string]$AzureFeed, [string]$FeedCredential ) + Write-Verbose -Verbose "In install-dotnet" + # This allows sudo install to be optional; needed when running in containers / as root # Note that when it is null, Invoke-Expression (but not &) must be used to interpolate properly $sudo = if (!$NoSudo) { "sudo" } - $installObtainUrl = "https://dotnet.microsoft.com/download/dotnet-core/scripts/v1" + # $installObtainUrl = "https://dot.net/v1" + $installObtainUrl = "https://dotnet.microsoft.com/download/dotnet/scripts/v1" $uninstallObtainUrl = "https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain" # Install for Linux and OS X if ($environment.IsLinux -or $environment.IsMacOS) { $wget = Get-Command -Name wget -CommandType Application -TotalCount 1 -ErrorAction Stop - # Uninstall all previous dotnet packages - $uninstallScript = if ($environment.IsLinux -and $environment.IsUbuntu) { - "dotnet-uninstall-debian-packages.sh" - } elseif ($environment.IsMacOS) { - "dotnet-uninstall-pkgs.sh" - } + # Attempt to uninstall previous dotnet packages if requested + if ($RemovePreviousVersion) { + $uninstallScript = if ($environment.IsLinux -and $environment.IsUbuntu) { + "dotnet-uninstall-debian-packages.sh" + } elseif ($environment.IsMacOS) { + "dotnet-uninstall-pkgs.sh" + } - if ($uninstallScript) { - Start-NativeExecution { - & $wget $uninstallObtainUrl/uninstall/$uninstallScript - Invoke-Expression "$sudo bash ./$uninstallScript" + if ($uninstallScript) { + Start-NativeExecution { + & $wget $uninstallObtainUrl/uninstall/$uninstallScript + Invoke-Expression "$sudo bash ./$uninstallScript" + } + } else { + Write-Warning "This script only removes prior versions of dotnet for Ubuntu and OS X" } - } else { - Write-Warning "This script only removes prior versions of dotnet for Ubuntu and OS X" } + Write-Verbose -Verbose "Invoking install script" + # Install new dotnet 1.1.0 preview packages $installScript = "dotnet-install.sh" - Start-NativeExecution { - Write-Verbose -Message "downloading install script from $installObtainUrl/$installScript ..." -Verbose + + Write-Verbose -Message "downloading install script from $installObtainUrl/$installScript ..." -Verbose & $wget $installObtainUrl/$installScript if ((Get-ChildItem "./$installScript").Length -eq 0) { @@ -1786,7 +1888,7 @@ function Install-Dotnet { } if ($Version) { - $bashArgs = @("./$installScript", '-v', $Version, '-q', $Quality) + $bashArgs = @("./$installScript", '-v', $Version) } elseif ($Channel) { $bashArgs = @("./$installScript", '-c', $Channel, '-q', $Quality) @@ -1797,9 +1899,18 @@ function Install-Dotnet { } if ($AzureFeed) { - $bashArgs += @('-AzureFeed', $AzureFeed, '-FeedCredential', $FeedCredential) + $bashArgs += @('-AzureFeed', $AzureFeed) } + if ($FeedCredential) { + $bashArgs += @('-FeedCredential', $FeedCredential) + } + + $bashArgs += @('-skipnonversionedfiles') + + $bashArgs | Out-String | Write-Verbose -Verbose + + Start-NativeExecution { bash @bashArgs } } elseif ($environment.IsWindows) { @@ -1807,13 +1918,11 @@ function Install-Dotnet { $installScript = "dotnet-install.ps1" Invoke-WebRequest -Uri $installObtainUrl/$installScript -OutFile $installScript if (-not $environment.IsCoreCLR) { - $installArgs = @{ - Quality = $Quality - } - + $installArgs = @{} if ($Version) { $installArgs += @{ Version = $Version } } elseif ($Channel) { + $installArgs += @{ Quality = $Quality } $installArgs += @{ Channel = $Channel } } @@ -1822,35 +1931,49 @@ function Install-Dotnet { } if ($AzureFeed) { - $installArgs += @{ - AzureFeed = $AzureFeed - $FeedCredential = $FeedCredential - } + $installArgs += @{AzureFeed = $AzureFeed} } + if ($FeedCredential) { + $installArgs += @{FeedCredential = $FeedCredential} + } + + $installArgs += @{ SkipNonVersionedFiles = $true } + + $installArgs | Out-String | Write-Verbose -Verbose + & ./$installScript @installArgs } else { # dotnet-install.ps1 uses APIs that are not supported in .NET Core, so we run it with Windows PowerShell $fullPSPath = Join-Path -Path $env:windir -ChildPath "System32\WindowsPowerShell\v1.0\powershell.exe" - $fullDotnetInstallPath = Join-Path -Path $PWD.Path -ChildPath $installScript - Start-NativeExecution { + $fullDotnetInstallPath = Join-Path -Path (Convert-Path -Path $PWD.Path) -ChildPath $installScript - if ($Version) { - $psArgs = @('-NoLogo', '-NoProfile', '-File', $fullDotnetInstallPath, '-Version', $Version, '-Quality', $Quality) - } - elseif ($Channel) { - $psArgs = @('-NoLogo', '-NoProfile', '-File', $fullDotnetInstallPath, '-Channel', $Channel, '-Quality', $Quality) - } + if ($Version) { + $psArgs = @('-NoLogo', '-NoProfile', '-File', $fullDotnetInstallPath, '-Version', $Version) + } + elseif ($Channel) { + $psArgs = @('-NoLogo', '-NoProfile', '-File', $fullDotnetInstallPath, '-Channel', $Channel, '-Quality', $Quality) + } - if ($InstallDir) { - $psArgs += @('-InstallDir', $InstallDir) - } + if ($InstallDir) { + $psArgs += @('-InstallDir', $InstallDir) + } - if ($AzureFeed) { - $psArgs += @('-AzureFeed', $AzureFeed, '-FeedCredential', $FeedCredential) - } + if ($AzureFeed) { + $psArgs += @('-AzureFeed', $AzureFeed) + } + + if ($FeedCredential) { + $psArgs += @('-FeedCredential', $FeedCredential) + } + $psArgs += @('-SkipNonVersionedFiles') + + # Removing the verbose message to not expose the secret + # $psArgs -join ' ' | Write-Verbose -Verbose + + Start-NativeExecution { & $fullPSPath @psArgs } } @@ -1858,15 +1981,52 @@ function Install-Dotnet { } function Get-RedHatPackageManager { - if ($environment.IsCentOS) { + if ($environment.IsCentOS -or (Get-Command -Name yum -CommandType Application -ErrorAction SilentlyContinue)) { "yum install -y -q" - } elseif ($environment.IsFedora) { + } elseif ($environment.IsFedora -or (Get-Command -Name dnf -CommandType Application -ErrorAction SilentlyContinue)) { "dnf install -y -q" } else { throw "Error determining package manager for this distribution." } } +function Install-GlobalGem { + param( + [Parameter()] + [string] + $Sudo = "", + + [Parameter(Mandatory)] + [string] + $GemName, + + [Parameter(Mandatory)] + [string] + $GemVersion + ) + try { + # We cannot guess if the user wants to run gem install as root on linux and windows, + # but macOs usually requires sudo + $gemsudo = '' + if($environment.IsMacOS -or $env:TF_BUILD) { + $gemsudo = $sudo + } + + Start-NativeExecution ([ScriptBlock]::Create("$gemsudo gem install $GemName -v $GemVersion --no-document")) + + } catch { + Write-Warning "Installation of gem $GemName $GemVersion failed! Must resolve manually." + $logs = Get-ChildItem "/var/lib/gems/*/extensions/x86_64-linux/*/$GemName-*/gem_make.out" | Select-Object -ExpandProperty FullName + foreach ($log in $logs) { + Write-Verbose "Contents of: $log" -Verbose + Get-Content -Raw -Path $log -ErrorAction Ignore | ForEach-Object { Write-Verbose $_ -Verbose } + Write-Verbose "END Contents of: $log" -Verbose + } + + throw + } +} + function Start-PSBootstrap { [CmdletBinding()] param( @@ -1884,6 +2044,10 @@ function Start-PSBootstrap { Push-Location $PSScriptRoot/tools + if ($dotnetSDKVersionOveride) { + $Version = $dotnetSDKVersionOveride + } + try { if ($environment.IsLinux -or $environment.IsMacOS) { # This allows sudo install to be optional; needed when running in containers / as root @@ -1899,19 +2063,14 @@ function Start-PSBootstrap { $Deps = @() if ($environment.IsLinux -and $environment.IsUbuntu) { # Build tools - $Deps += "curl", "g++", "cmake", "make" - - if ($BuildLinuxArm) { - $Deps += "gcc-arm-linux-gnueabihf", "g++-arm-linux-gnueabihf" - } + $Deps += "curl", "wget" # .NET Core required runtime libraries - $Deps += "libunwind8" if ($environment.IsUbuntu16) { $Deps += "libicu55" } elseif ($environment.IsUbuntu18) { $Deps += "libicu60"} # Packaging tools - if ($Package) { $Deps += "ruby-dev", "groff", "libffi-dev" } + if ($Package) { $Deps += "ruby-dev", "groff", "libffi-dev", "rpm", "g++", "make" } # Install dependencies # change the fontend from apt-get to noninteractive @@ -1929,13 +2088,13 @@ function Start-PSBootstrap { } } elseif ($environment.IsLinux -and $environment.IsRedHatFamily) { # Build tools - $Deps += "which", "curl", "gcc-c++", "cmake", "make" + $Deps += "which", "curl", "wget" # .NET Core required runtime libraries - $Deps += "libicu", "libunwind" + $Deps += "libicu", "openssl-libs" # Packaging tools - if ($Package) { $Deps += "ruby-devel", "rpm-build", "groff", 'libffi-devel' } + if ($Package) { $Deps += "ruby-devel", "rpm-build", "groff", 'libffi-devel', "gcc-c++" } $PackageManager = Get-RedHatPackageManager @@ -1953,10 +2112,10 @@ function Start-PSBootstrap { } } elseif ($environment.IsLinux -and $environment.IsSUSEFamily) { # Build tools - $Deps += "gcc", "cmake", "make" + $Deps += "wget" # Packaging tools - if ($Package) { $Deps += "ruby-devel", "rpmbuild", "groff", 'libffi-devel' } + if ($Package) { $Deps += "ruby-devel", "rpmbuild", "groff", 'libffi-devel', "gcc" } $PackageManager = "zypper --non-interactive install" $baseCommand = "$sudo $PackageManager" @@ -1978,8 +2137,8 @@ function Start-PSBootstrap { $PackageManager = "$sudo port" } - # Build tools - $Deps += "cmake" + # wget for downloading dotnet + $Deps += "wget" # .NET Core required runtime libraries $Deps += "openssl" @@ -1988,7 +2147,7 @@ function Start-PSBootstrap { # ignore exitcode, because they may be already installed Start-NativeExecution ([ScriptBlock]::Create("$PackageManager install $Deps")) -IgnoreExitcode } elseif ($environment.IsLinux -and $environment.IsAlpine) { - $Deps += 'libunwind', 'libcurl', 'bash', 'cmake', 'clang', 'build-base', 'git', 'curl' + $Deps += 'libunwind', 'libcurl', 'bash', 'build-base', 'git', 'curl', 'wget' Start-NativeExecution { Invoke-Expression "apk add $Deps" @@ -1997,30 +2156,24 @@ function Start-PSBootstrap { # Install [fpm](https://github.com/jordansissel/fpm) and [ronn](https://github.com/rtomayko/ronn) if ($Package) { - try { - # We cannot guess if the user wants to run gem install as root on linux and windows, - # but macOs usually requires sudo - $gemsudo = '' - if($environment.IsMacOS -or $env:TF_BUILD) { - $gemsudo = $sudo - } - Start-NativeExecution ([ScriptBlock]::Create("$gemsudo gem install ffi -v 1.12.0 --no-document")) - Start-NativeExecution ([ScriptBlock]::Create("$gemsudo gem install fpm -v 1.11.0 --no-document")) - Start-NativeExecution ([ScriptBlock]::Create("$gemsudo gem install ronn -v 0.7.3 --no-document")) - } catch { - Write-Warning "Installation of fpm and ronn gems failed! Must resolve manually." - } + Install-GlobalGem -Sudo $sudo -GemName "ffi" -GemVersion "1.12.0" + Install-GlobalGem -Sudo $sudo -GemName "fpm" -GemVersion "1.11.0" + Install-GlobalGem -Sudo $sudo -GemName "ronn" -GemVersion "0.7.3" } } + Write-Verbose -Verbose "Calling Find-Dotnet from Start-PSBootstrap" + # Try to locate dotnet-SDK before installing it Find-Dotnet + Write-Verbose -Verbose "Back from calling Find-Dotnet from Start-PSBootstrap" + # Install dotnet-SDK $dotNetExists = precheck 'dotnet' $null $dotNetVersion = [string]::Empty if($dotNetExists) { - $dotNetVersion = Start-NativeExecution -sb { dotnet --version } -IgnoreExitcode + $dotNetVersion = Find-RequiredSDK $dotnetCLIRequiredVersion } if(!$dotNetExists -or $dotNetVersion -ne $dotnetCLIRequiredVersion -or $Force.IsPresent) { @@ -2035,6 +2188,12 @@ function Start-PSBootstrap { } $DotnetArguments = @{ Channel=$Channel; Version=$Version; NoSudo=$NoSudo } + + if ($dotnetAzureFeed) { + $null = $DotnetArguments.Add("AzureFeed", $dotnetAzureFeed) + $null = $DotnetArguments.Add("FeedCredential", $dotnetAzureFeedSecret) + } + Install-Dotnet @DotnetArguments } else { @@ -2056,13 +2215,39 @@ function Start-PSBootstrap { } } +## If the required SDK version is found, return it. +## Otherwise, return the latest installed SDK version that can be found. +function Find-RequiredSDK { + param( + [Parameter(Mandatory, Position = 0)] + [string] $requiredSdkVersion + ) + + $output = Start-NativeExecution -sb { dotnet --list-sdks } -IgnoreExitcode 2> $null + + $installedSdkVersions = $output | ForEach-Object { + # this splits strings like + # '6.0.202 [C:\Program Files\dotnet\sdk]' + # '7.0.100-preview.2.22153.17 [C:\Users\johndoe\AppData\Local\Microsoft\dotnet\sdk]' + # into version and path parts. + ($_ -split '\s',2)[0] + } + + if ($installedSdkVersions -contains $requiredSdkVersion) { + $requiredSdkVersion + } + else { + $installedSdkVersions | Sort-Object -Descending | Select-Object -First 1 + } +} + function Start-DevPowerShell { [CmdletBinding(DefaultParameterSetName='ConfigurationParamSet')] param( [string[]]$ArgumentList = @(), [switch]$LoadProfile, [Parameter(ParameterSetName='ConfigurationParamSet')] - [ValidateSet("Debug", "Release", "CodeCoverage", '')] # should match New-PSOptions -Configuration values + [ValidateSet('Debug', 'Release', 'CodeCoverage', 'StaticAnalysis', '')] # should match New-PSOptions -Configuration values [string]$Configuration, [Parameter(ParameterSetName='BinDirParamSet')] [string]$BinDir, @@ -2101,7 +2286,7 @@ function Start-DevPowerShell { # splatting for the win $startProcessArgs = @{ - FilePath = "$BinDir\pwsh" + FilePath = Join-Path $BinDir 'pwsh' } if ($ArgumentList) { @@ -2187,21 +2372,45 @@ function Start-ResGen } } -function Find-Dotnet() { +function Find-Dotnet { + param ( + [switch] $SetDotnetRoot + ) + + Write-Verbose "In Find-DotNet" + $originalPath = $env:PATH $dotnetPath = if ($environment.IsWindows) { "$env:LocalAppData\Microsoft\dotnet" } else { "$env:HOME/.dotnet" } + $chosenDotNetVersion = if($dotnetSDKVersionOveride) { + $dotnetSDKVersionOveride + } + else { + $dotnetCLIRequiredVersion + } + # If there dotnet is already in the PATH, check to see if that version of dotnet can find the required SDK # This is "typically" the globally installed dotnet if (precheck dotnet) { # Must run from within repo to ensure global.json can specify the required SDK version Push-Location $PSScriptRoot - $dotnetCLIInstalledVersion = Start-NativeExecution -sb { dotnet --version } -IgnoreExitcode 2> $null + $dotnetCLIInstalledVersion = Find-RequiredSDK $chosenDotNetVersion Pop-Location - if ($dotnetCLIInstalledVersion -ne $dotnetCLIRequiredVersion) { + + Write-Verbose -Message "Find-DotNet: dotnetCLIInstalledVersion = $dotnetCLIInstalledVersion; chosenDotNetVersion = $chosenDotNetVersion" + + if ($dotnetCLIInstalledVersion -ne $chosenDotNetVersion) { Write-Warning "The 'dotnet' in the current path can't find SDK version ${dotnetCLIRequiredVersion}, prepending $dotnetPath to PATH." # Globally installed dotnet doesn't have the required SDK version, prepend the user local dotnet location $env:PATH = $dotnetPath + [IO.Path]::PathSeparator + $env:PATH + + if ($SetDotnetRoot) { + Write-Verbose -Verbose "Setting DOTNET_ROOT to $dotnetPath" + $env:DOTNET_ROOT = $dotnetPath + } + } elseif ($SetDotnetRoot) { + Write-Verbose -Verbose "Expected dotnet version found, setting DOTNET_ROOT to $dotnetPath" + $env:DOTNET_ROOT = $dotnetPath } } else { @@ -2296,258 +2505,6 @@ function script:precheck([string]$command, [string]$missedMessage) { } } -# this function wraps native command Execution -# for more information, read https://mnaoumov.wordpress.com/2015/01/11/execution-of-external-commands-in-powershell-done-right/ -function script:Start-NativeExecution -{ - param( - [scriptblock]$sb, - [switch]$IgnoreExitcode, - [switch]$VerboseOutputOnError - ) - $backupEAP = $ErrorActionPreference - $ErrorActionPreference = "Continue" - try { - if($VerboseOutputOnError.IsPresent) - { - $output = & $sb 2>&1 - } - else - { - & $sb - } - - # note, if $sb doesn't have a native invocation, $LASTEXITCODE will - # point to the obsolete value - if ($LASTEXITCODE -ne 0 -and -not $IgnoreExitcode) { - if($VerboseOutputOnError.IsPresent -and $output) - { - $output | Out-String | Write-Verbose -Verbose - } - - # Get caller location for easier debugging - $caller = Get-PSCallStack -ErrorAction SilentlyContinue - if($caller) - { - $callerLocationParts = $caller[1].Location -split ":\s*line\s*" - $callerFile = $callerLocationParts[0] - $callerLine = $callerLocationParts[1] - - $errorMessage = "Execution of {$sb} by ${callerFile}: line $callerLine failed with exit code $LASTEXITCODE" - throw $errorMessage - } - throw "Execution of {$sb} failed with exit code $LASTEXITCODE" - } - } finally { - $ErrorActionPreference = $backupEAP - } -} - -function Start-CrossGen { - [CmdletBinding()] - param( - [Parameter(Mandatory= $true)] - [ValidateNotNullOrEmpty()] - [String] - $PublishPath, - - [Parameter(Mandatory=$true)] - [ValidateSet("alpine-x64", - "linux-arm", - "linux-arm64", - "linux-x64", - "osx-arm64", - "osx-x64", - "win-arm", - "win-arm64", - "win7-x64", - "win7-x86")] - [string] - $Runtime - ) - - function New-CrossGenAssembly { - param ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [String[]] - $AssemblyPath, - - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [String] - $CrossgenPath, - - [Parameter(Mandatory = $true)] - [ValidateSet("alpine-x64", - "linux-arm", - "linux-arm64", - "linux-x64", - "osx-arm64", - "osx-x64", - "win-arm", - "win-arm64", - "win7-x64", - "win7-x86")] - [string] - $Runtime - ) - - $platformAssembliesPath = Split-Path $AssemblyPath[0] -Parent - - $targetOS, $targetArch = $Runtime -split '-' - - # Special cases where OS / Arch does not conform with runtime names - switch ($Runtime) { - 'alpine-x64' { - $targetOS = 'linux' - $targetArch = 'x64' - } - 'win-arm' { - $targetOS = 'windows' - $targetArch = 'arm' - } - 'win-arm64' { - $targetOS = 'windows' - $targetArch = 'arm64' - } - 'win7-x64' { - $targetOS = 'windows' - $targetArch = 'x64' - } - 'win7-x86' { - $targetOS = 'windows' - $targetArch = 'x86' - } - } - - $generatePdb = $targetos -eq 'windows' - - # The path to folder must end with directory separator - $dirSep = [System.IO.Path]::DirectorySeparatorChar - $platformAssembliesPath = if (-not $platformAssembliesPath.EndsWith($dirSep)) { $platformAssembliesPath + $dirSep } - - Start-NativeExecution { - $crossgen2Params = @( - "-r" - $platformAssembliesPath - "--out-near-input" - "--single-file-compilation" - "-O" - "--targetos" - $targetOS - "--targetarch" - $targetArch - ) - - if ($generatePdb) { - $crossgen2Params += "--pdb" - } - - $crossgen2Params += $AssemblyPath - - & $CrossgenPath $crossgen2Params - } - } - - if (-not (Test-Path $PublishPath)) { - throw "Path '$PublishPath' does not exist." - } - - # Get the path to crossgen - $crossGenExe = if ($environment.IsWindows) { "crossgen2.exe" } else { "crossgen2" } - - # The crossgen tool is only published for these particular runtimes - $crossGenRuntime = if ($environment.IsWindows) { - # for windows the tool architecture is the host machine architecture, so it is always x64. - # we can cross compile for x86, arm and arm64 - "win-x64" - } else { - $Runtime - } - - if (-not $crossGenRuntime) { - throw "crossgen is not available for this platform" - } - - $dotnetRuntimeVersion = $script:Options.Framework -replace 'net' - - # Get the CrossGen.exe for the correct runtime with the latest version - $crossGenPath = Get-ChildItem $script:Environment.nugetPackagesRoot $crossGenExe -Recurse | ` - Where-Object { $_.FullName -match $crossGenRuntime } | ` - Where-Object { $_.FullName -match $dotnetRuntimeVersion } | ` - Where-Object { (Split-Path $_.FullName -Parent).EndsWith('tools') } | ` - Sort-Object -Property FullName -Descending | ` - Select-Object -First 1 | ` - ForEach-Object { $_.FullName } - if (-not $crossGenPath) { - throw "Unable to find latest version of crossgen2.exe. 'Please run Start-PSBuild -Clean' first, and then try again." - } - Write-Verbose "Matched CrossGen2.exe: $crossGenPath" -Verbose - - # Common assemblies used by Add-Type or assemblies with high JIT and no pdbs to crossgen - $commonAssembliesForAddType = @( - "Microsoft.CodeAnalysis.CSharp.dll" - "Microsoft.CodeAnalysis.dll" - "System.Linq.Expressions.dll" - "Microsoft.CSharp.dll" - "System.Runtime.Extensions.dll" - "System.Linq.dll" - "System.Collections.Concurrent.dll" - "System.Collections.dll" - "Newtonsoft.Json.dll" - "System.IO.FileSystem.dll" - "System.Diagnostics.Process.dll" - "System.Threading.Tasks.Parallel.dll" - "System.Security.AccessControl.dll" - "System.Text.Encoding.CodePages.dll" - "System.Private.Uri.dll" - "System.Threading.dll" - "System.Security.Principal.Windows.dll" - "System.Console.dll" - "Microsoft.Win32.Registry.dll" - "System.IO.Pipes.dll" - "System.Diagnostics.FileVersionInfo.dll" - "System.Collections.Specialized.dll" - "Microsoft.ApplicationInsights.dll" - ) - - $fullAssemblyList = $commonAssembliesForAddType - - $assemblyFullPaths = @() - $assemblyFullPaths += foreach ($assemblyName in $fullAssemblyList) { - Join-Path $PublishPath $assemblyName - } - - New-CrossGenAssembly -CrossgenPath $crossGenPath -AssemblyPath $assemblyFullPaths -Runtime $Runtime - - # - # With the latest dotnet.exe, the default load context is only able to load TPAs, and TPA - # only contains IL assembly names. In order to make the default load context able to load - # the NI PS assemblies, we need to replace the IL PS assemblies with the corresponding NI - # PS assemblies, but with the same IL assembly names. - # - Write-Verbose "PowerShell Ngen assemblies have been generated. Deploying ..." -Verbose - foreach ($assemblyName in $fullAssemblyList) { - - # Remove the IL assembly and its symbols. - $assemblyPath = Join-Path $PublishPath $assemblyName - $symbolsPath = [System.IO.Path]::ChangeExtension($assemblyPath, ".pdb") - - Remove-Item $assemblyPath -Force -ErrorAction Stop - - # Rename the corresponding ni.dll assembly to be the same as the IL assembly - $niAssemblyPath = [System.IO.Path]::ChangeExtension($assemblyPath, "ni.dll") - Rename-Item $niAssemblyPath $assemblyPath -Force -ErrorAction Stop - - # No symbols are available for Microsoft.CodeAnalysis.CSharp.dll, Microsoft.CodeAnalysis.dll, - # Microsoft.CodeAnalysis.VisualBasic.dll, and Microsoft.CSharp.dll. - if ($commonAssembliesForAddType -notcontains $assemblyName) { - Remove-Item $symbolsPath -Force -ErrorAction Stop - } - } -} - # Cleans the PowerShell repo - everything but the root folder function Clear-PSRepo { @@ -2914,7 +2871,6 @@ assembly # only create an assembly group if we have tests if ( $tCases.count -eq 0 -and ! $includeEmpty ) { continue } $tGroup = $tCases | Group-Object result - $total = $tCases.Count $asm = [testassembly]::new() $asm.environment = $environment $asm."run-date" = $rundate @@ -3070,7 +3026,6 @@ function Restore-PSOptions { -RootInfo $options.RootInfo ` -Top $options.Top ` -Runtime $options.Runtime ` - -Crossgen $options.Crossgen ` -Configuration $options.Configuration ` -PSModuleRestore $options.PSModuleRestore ` -Framework $options.Framework ` @@ -3094,10 +3049,6 @@ function New-PSOptionsObject [String] $Runtime, - [Parameter(Mandatory)] - [Bool] - $CrossGen, - [Parameter(Mandatory)] [String] $Configuration, @@ -3126,7 +3077,6 @@ function New-PSOptionsObject Framework = $Framework Runtime = $Runtime Output = $Output - CrossGen = $CrossGen PSModuleRestore = $PSModuleRestore ForMinimalSize = $ForMinimalSize } @@ -3406,3 +3356,97 @@ function Set-CorrectLocale # Output the locale to log it locale } + +function Install-AzCopy { + $testPath = "C:\Program Files (x86)\Microsoft SDKs\Azure\AzCopy\AzCopy.exe" + if (Test-Path $testPath) { + Write-Verbose "AzCopy already installed" -Verbose + return + } + + $destination = "$env:TEMP\azcopy10.zip" + $downloadLocation = (Invoke-WebRequest -Uri https://aka.ms/downloadazcopy-v10-windows -MaximumRedirection 0 -ErrorAction SilentlyContinue -SkipHttpErrorCheck).headers.location | Select-Object -First 1 + + Invoke-WebRequest -Uri $downloadLocation -OutFile $destination -Verbose + Expand-archive -Path $destination -Destinationpath '$(Agent.ToolsDirectory)\azcopy10' +} + +function Find-AzCopy { + $searchPaths = @('$(Agent.ToolsDirectory)\azcopy10\AzCopy.exe', "C:\Program Files (x86)\Microsoft SDKs\Azure\AzCopy\AzCopy.exe", "C:\azcopy10\AzCopy.exe") + + foreach ($filter in $searchPaths) { + $azCopy = Get-ChildItem -Path $filter -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName -First 1 + if ($azCopy) { + return $azCopy + } + } + + $azCopy = Get-Command -Name azCopy -ErrorAction Stop | Select-Object -First 1 + return $azCopy.Path +} + +function Clear-NativeDependencies +{ + param( + [Parameter(Mandatory=$true)] [string] $PublishFolder + ) + + $diasymFileNamePattern = 'microsoft.diasymreader.native.{0}.dll' + + switch -regex ($($script:Options.Runtime)) { + '.*-x64' { + $diasymFileName = $diasymFileNamePattern -f 'amd64' + } + '.*-x86' { + $diasymFileName = $diasymFileNamePattern -f 'x86' + } + '.*-arm' { + $diasymFileName = $diasymFileNamePattern -f 'arm' + } + '.*-arm64' { + $diasymFileName = $diasymFileNamePattern -f 'arm64' + } + 'fxdependent.*' { + Write-Verbose -Message "$($script:Options.Runtime) is a fxdependent runtime, no cleanup needed in pwsh.deps.json" -Verbose + return + } + Default { + throw "Unknown runtime $($script:Options.Runtime)" + } + } + + $filesToDeleteCore = @($diasymFileName) + + ## Currently we do not need to remove any files from WinDesktop runtime. + $filesToDeleteWinDesktop = @() + + $deps = Get-Content "$PublishFolder/pwsh.deps.json" -Raw | ConvertFrom-Json -Depth 20 + $targetRuntime = ".NETCoreApp,Version=v7.0/$($script:Options.Runtime)" + + $runtimePackNetCore = $deps.targets.${targetRuntime}.PSObject.Properties.Name -like 'runtimepack.Microsoft.NETCore.App.Runtime*' + $runtimePackWinDesktop = $deps.targets.${targetRuntime}.PSObject.Properties.Name -like 'runtimepack.Microsoft.WindowsDesktop.App.Runtime*' + + if ($runtimePackNetCore) + { + $filesToDeleteCore | ForEach-Object { + Write-Verbose "Removing $_ from pwsh.deps.json" -Verbose + $deps.targets.${targetRuntime}.${runtimePackNetCore}.native.PSObject.Properties.Remove($_) + if (Test-Path $PublishFolder/$_) { + Remove-Item -Path $PublishFolder/$_ -Force -Verbose + } + } + } + + if ($runtimePackWinDesktop) + { + $filesToDeleteWinDesktop | ForEach-Object { + Write-Verbose "Removing $_ from pwsh.deps.json" -Verbose + $deps.targets.${targetRuntime}.${runtimePackWinDesktop}.native.PSObject.Properties.Remove($_) + if (Test-Path $PublishFolder/$_) { + Remove-Item -Path $PublishFolder/$_ -Force -Verbose + } + } + } + + $deps | ConvertTo-Json -Depth 20 | Set-Content "$PublishFolder/pwsh.deps.json" -Force +} diff --git a/demos/DSC/readme.md b/demos/DSC/readme.md index 0ce83fb3efa..0dd98968d2f 100644 --- a/demos/DSC/readme.md +++ b/demos/DSC/readme.md @@ -1,6 +1,6 @@ # DSC MOF Compilation Demo -[PowerShell Desired State Configuration](https://docs.microsoft.com/powershell/scripting/dsc/overview/overview) is a declarative configuration platform for Windows and Linux. +[PowerShell Desired State Configuration](https://docs.microsoft.com/powershell/scripting/dsc/overview) is a declarative configuration platform for Windows and Linux. DSC configurations can be authored in PowerShell and compiled into the resultant MOF document. This demo shows use of PowerShell to author a DSC configuration to set the configuration of an Apache web server. PowerShell scripting is used to assess distribution and version-specific properties, diff --git a/docker/tests/README.md b/docker/tests/README.md deleted file mode 100644 index 716a7d6f62e..00000000000 --- a/docker/tests/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Docker tests - -## Windows and Linux containers - -The tests must be run separately on the Windows and Linux docker daemons. You can use the Linux docker daemon on Windows, but that will only test Linux containers not Windows Containers. - -## To building and basic behavior of the containers - -```PowerShell -Invoke-Pester -``` - -Note: be sure to do this using both the Windows and Linux docker daemon. - -## To test the productions containers - -```PowerShell -Invoke-Pester -Tag Behavior -``` - -## To test only building the containers - -```PowerShell -Invoke-Pester -Tag Build -``` diff --git a/docker/tests/Templates/centos7/Dockerfile b/docker/tests/Templates/centos7/Dockerfile deleted file mode 100644 index dddf2439445..00000000000 --- a/docker/tests/Templates/centos7/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM centos:7 - -ARG PACKAGENAME -ARG PACKAGELOCATION -ARG PREVIEWSUFFIX= -ARG TESTLIST=/PowerShell/test/powershell/Modules/PackageManagement/PackageManagement.Tests.ps1,/PowerShell/test/powershell/engine/Module -ARG TESTDOWNLOADCOMMAND="git clone --recursive https://github.com/PowerShell/PowerShell.git" - -# Install dependencies -RUN yum install -y \ - curl \ - glibc-locale-source \ - git - -# Setup the locale -ENV LANG en_US.UTF-8 -ENV LC_ALL $LANG -RUN localedef --charmap=UTF-8 --inputfile=en_US $LANG - -RUN curl -L -o $PACKAGENAME $PACKAGELOCATION/$PACKAGENAME \ - && yum install -y $PACKAGENAME -RUN $TESTDOWNLOADCOMMAND -RUN pwsh$PREVIEWSUFFIX -c "Import-Module /PowerShell/build.psm1;\$dir='/usr/local/share/powershell/Modules';\$null=New-Item -Type Directory -Path \$dir -ErrorAction SilentlyContinue;Restore-PSPester -Destination \$dir;exit (Invoke-Pester $TESTLIST -PassThru).FailedCount" diff --git a/docker/tests/Templates/debian.9/Dockerfile b/docker/tests/Templates/debian.9/Dockerfile deleted file mode 100644 index f2de0f8d61f..00000000000 --- a/docker/tests/Templates/debian.9/Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -FROM debian:stretch - -ARG PACKAGENAME -ARG PACKAGELOCATION -ARG PREVIEWSUFFIX= -ARG TESTLIST=/PowerShell/test/powershell/Modules/PackageManagement/PackageManagement.Tests.ps1,/PowerShell/test/powershell/engine/Module -ARG TESTDOWNLOADCOMMAND="git clone --recursive https://github.com/PowerShell/PowerShell.git" - -# Install dependencies -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - apt-utils \ - ca-certificates \ - curl \ - apt-transport-https \ - locales \ - git \ - && apt-get clean - -# Setup the locale -ENV LANG en_US.UTF-8 -ENV LC_ALL $LANG -RUN locale-gen $LANG && update-locale - -RUN curl -L -o $PACKAGENAME $PACKAGELOCATION/$PACKAGENAME -RUN dpkg -i $PACKAGENAME || : -RUN apt-get install -y -f --no-install-recommends -RUN $TESTDOWNLOADCOMMAND -RUN pwsh$PREVIEWSUFFIX -c "Import-Module /PowerShell/build.psm1;\$dir='/usr/local/share/powershell/Modules';\$null=New-Item -Type Directory -Path \$dir -ErrorAction SilentlyContinue;Restore-PSPester -Destination \$dir;exit (Invoke-Pester $TESTLIST -PassThru).FailedCount" diff --git a/docker/tests/Templates/fedora28/Dockerfile b/docker/tests/Templates/fedora28/Dockerfile deleted file mode 100644 index 9c9f958f54d..00000000000 --- a/docker/tests/Templates/fedora28/Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -FROM fedora:28 - -ARG PACKAGENAME -ARG PACKAGELOCATION -ARG PREVIEWSUFFIX= -ARG TESTLIST=/PowerShell/test/powershell/Modules/PackageManagement/PackageManagement.Tests.ps1,/PowerShell/test/powershell/engine/Module -ARG TESTDOWNLOADCOMMAND="git clone --recursive https://github.com/PowerShell/PowerShell.git" - -# Install dependencies -RUN dnf install -y \ - curl \ - glibc-locale-source \ - git \ - compat-openssl10 \ - && dnf upgrade-minimal -y --security \ - && dnf clean all - -# Setup the locale -ENV LANG en_US.UTF-8 -ENV LC_ALL $LANG -RUN localedef --charmap=UTF-8 --inputfile=en_US $LANG - -RUN curl -L -o $PACKAGENAME $PACKAGELOCATION/$PACKAGENAME -RUN dnf install -y $PACKAGENAME -RUN $TESTDOWNLOADCOMMAND -RUN pwsh$PREVIEWSUFFIX -c "Import-Module /PowerShell/build.psm1;\$dir='/usr/local/share/powershell/Modules';\$null=New-Item -Type Directory -Path \$dir -ErrorAction SilentlyContinue;Restore-PSPester -Destination \$dir;exit (Invoke-Pester $TESTLIST -PassThru).FailedCount" diff --git a/docker/tests/Templates/fxdependent-centos7/Dockerfile b/docker/tests/Templates/fxdependent-centos7/Dockerfile deleted file mode 100644 index 10cde0cd125..00000000000 --- a/docker/tests/Templates/fxdependent-centos7/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -FROM centos:7 - -ARG PACKAGENAME -ARG PACKAGELOCATION -ARG PREVIEWSUFFIX= -ARG TESTLIST=/PowerShell/test/powershell/Modules/PackageManagement/PackageManagement.Tests.ps1,/PowerShell/test/powershell/engine/Module -ARG TESTDOWNLOADCOMMAND="git clone --recursive https://github.com/PowerShell/PowerShell.git" - -# Install dependencies -RUN yum install -y \ - glibc-locale-source \ - git - -# Install dotnet-runtime -RUN rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm -RUN yum install -y \ - dotnet-runtime-2.1 - -# Setup the locale -ENV LANG en_US.UTF-8 -ENV LC_ALL $LANG -RUN localedef --charmap=UTF-8 --inputfile=en_US $LANG - -# Install PowerShell package -ADD $PACKAGELOCATION/$PACKAGENAME . -RUN mkdir -p /opt/microsoft/powershell -RUN tar zxf $PACKAGENAME -C /opt/microsoft/powershell - -# Download and run tests -RUN $TESTDOWNLOADCOMMAND -RUN dotnet /opt/microsoft/powershell/pwsh.dll -c "Import-Module /PowerShell/build.psm1;\$dir='/usr/local/share/powershell/Modules';\$null=New-Item -Type Directory -Path \$dir -ErrorAction SilentlyContinue;Restore-PSPester -Destination \$dir;exit (Invoke-Pester $TESTLIST -PassThru).FailedCount" diff --git a/docker/tests/Templates/fxdependent-debian.9/Dockerfile b/docker/tests/Templates/fxdependent-debian.9/Dockerfile deleted file mode 100644 index 2d7beff913b..00000000000 --- a/docker/tests/Templates/fxdependent-debian.9/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -FROM microsoft/dotnet:2.1.7-runtime-stretch-slim - -ARG PACKAGENAME -ARG PACKAGELOCATION -ARG PREVIEWSUFFIX= -ARG TESTLIST=/PowerShell/test/powershell/Modules/PackageManagement/PackageManagement.Tests.ps1,/PowerShell/test/powershell/engine/Module -ARG TESTDOWNLOADCOMMAND="git clone --recursive https://github.com/PowerShell/PowerShell.git" - -# Install dependencies -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - apt-utils \ - ca-certificates \ - apt-transport-https \ - locales \ - git \ - && apt-get clean - -# Setup the locale -ENV LANG en_US.UTF-8 -ENV LC_ALL $LANG -RUN locale-gen $LANG && update-locale - -# Install PowerShell package -ADD $PACKAGELOCATION/$PACKAGENAME . -RUN mkdir -p /opt/microsoft/powershell -RUN tar zxf $PACKAGENAME -C /opt/microsoft/powershell - -# Download and run tests -RUN $TESTDOWNLOADCOMMAND -RUN dotnet /opt/microsoft/powershell/pwsh.dll -c "Import-Module /PowerShell/build.psm1;\$dir='/usr/local/share/powershell/Modules';\$null=New-Item -Type Directory -Path \$dir -ErrorAction SilentlyContinue;Restore-PSPester -Destination \$dir;exit (Invoke-Pester $TESTLIST -PassThru).FailedCount" diff --git a/docker/tests/Templates/fxdependent-dotnetsdk-latest/Dockerfile b/docker/tests/Templates/fxdependent-dotnetsdk-latest/Dockerfile deleted file mode 100644 index cf56e4ec074..00000000000 --- a/docker/tests/Templates/fxdependent-dotnetsdk-latest/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -FROM microsoft/dotnet:3.0.100-preview-sdk - -ARG PACKAGENAME -ARG PACKAGELOCATION -ARG PREVIEWSUFFIX= -ARG TESTLIST=/PowerShell/test/powershell/Modules/PackageManagement/PackageManagement.Tests.ps1,/PowerShell/test/powershell/engine/Module -ARG TESTDOWNLOADCOMMAND="git clone --recursive https://github.com/PowerShell/PowerShell.git" - -# Install dependencies -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - apt-utils \ - ca-certificates \ - apt-transport-https \ - locales \ - git \ - && apt-get clean - -# Setup the locale -ENV LANG en_US.UTF-8 -ENV LC_ALL $LANG -RUN locale-gen $LANG && update-locale - -# Install PowerShell package -ADD $PACKAGELOCATION/$PACKAGENAME . -RUN mkdir -p /opt/microsoft/powershell \ - && tar zxf $PACKAGENAME -C /opt/microsoft/powershell - -# Download and run tests -RUN $TESTDOWNLOADCOMMAND -RUN dotnet /opt/microsoft/powershell/pwsh.dll -c "Import-Module /PowerShell/build.psm1;\$dir='/usr/local/share/powershell/Modules';\$null=New-Item -Type Directory -Path \$dir -ErrorAction SilentlyContinue;Restore-PSPester -Destination \$dir;exit (Invoke-Pester $TESTLIST -PassThru).FailedCount" diff --git a/docker/tests/Templates/fxdependent-fedora28/Dockerfile b/docker/tests/Templates/fxdependent-fedora28/Dockerfile deleted file mode 100644 index 6acb7452446..00000000000 --- a/docker/tests/Templates/fxdependent-fedora28/Dockerfile +++ /dev/null @@ -1,38 +0,0 @@ -FROM fedora:28 - -ARG PACKAGENAME -ARG PACKAGELOCATION -ARG PREVIEWSUFFIX= -ARG TESTLIST=/PowerShell/test/powershell/Modules/PackageManagement/PackageManagement.Tests.ps1,/PowerShell/test/powershell/engine/Module -ARG TESTDOWNLOADCOMMAND="git clone --recursive https://github.com/PowerShell/PowerShell.git" - -# Install dependencies -RUN dnf install -y \ - glibc-locale-source \ - git \ - compat-openssl10 \ - && dnf upgrade-minimal -y --security \ - && dnf clean all - -# Install dotnet-runtime -RUN rpm --import https://packages.microsoft.com/keys/microsoft.asc -ADD https://packages.microsoft.com/config/fedora/27/prod.repo . -RUN mv prod.repo /etc/yum.repos.d/microsoft-prod.repo -RUN dnf install -y \ - dotnet-runtime-2.1 \ - && dnf upgrade-minimal -y --security \ - && dnf clean all - -# Setup the locale -ENV LANG en_US.UTF-8 -ENV LC_ALL $LANG -RUN localedef --charmap=UTF-8 --inputfile=en_US $LANG - -# Install PowerShell package -ADD $PACKAGELOCATION/$PACKAGENAME . -RUN mkdir -p /opt/microsoft/powershell \ - && tar zxf $PACKAGENAME -C /opt/microsoft/powershell - -# Download and run tests -RUN $TESTDOWNLOADCOMMAND -RUN dotnet /opt/microsoft/powershell/pwsh.dll -c "Import-Module /PowerShell/build.psm1;\$dir='/usr/local/share/powershell/Modules';\$null=New-Item -Type Directory -Path \$dir -ErrorAction SilentlyContinue;Restore-PSPester -Destination \$dir;exit (Invoke-Pester $TESTLIST -PassThru).FailedCount" diff --git a/docker/tests/Templates/fxdependent-opensuse42.3/Dockerfile b/docker/tests/Templates/fxdependent-opensuse42.3/Dockerfile deleted file mode 100644 index 74b4bc98256..00000000000 --- a/docker/tests/Templates/fxdependent-opensuse42.3/Dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -FROM opensuse:42.3 - -ARG PACKAGENAME -ARG PACKAGELOCATION -ARG PREVIEWSUFFIX= -ARG TESTLIST=/PowerShell/test/powershell/Modules/PackageManagement/PackageManagement.Tests.ps1,/PowerShell/test/powershell/engine/Module -ARG TESTDOWNLOADCOMMAND="git clone --recursive https://github.com/PowerShell/PowerShell.git" - -# Install dependencies -RUN zypper --non-interactive update --skip-interactive \ - && zypper --non-interactive install \ - glibc-locale \ - glibc-i18ndata \ - tar \ - libunwind \ - libicu \ - openssl \ - git - -# Install dotnet-runtime -ADD https://packages.microsoft.com/keys/microsoft.asc . -RUN rpmkeys --import microsoft.asc -ADD https://packages.microsoft.com/config/opensuse/42.2/prod.repo . -RUN mv prod.repo /etc/zypp/repos.d/microsoft-prod.repo -RUN zypper --non-interactive update --skip-interactive \ - && zypper --non-interactive install \ - dotnet-runtime-2.1 - -# Setup the locale -ENV LANG en_US.UTF-8 -ENV LC_ALL $LANG -RUN localedef --charmap=UTF-8 --inputfile=en_US $LANG - -# Install PowerShell package -ADD $PACKAGELOCATION/$PACKAGENAME . -RUN mkdir -p /opt/microsoft/powershell -RUN tar zxf $PACKAGENAME -C /opt/microsoft/powershell - -# Download and run tests -RUN $TESTDOWNLOADCOMMAND -RUN dotnet /opt/microsoft/powershell/pwsh.dll -c "Import-Module /PowerShell/build.psm1;\$dir='/usr/local/share/powershell/Modules';\$null=New-Item -Type Directory -Path \$dir -ErrorAction SilentlyContinue;Restore-PSPester -Destination \$dir;exit (Invoke-Pester $TESTLIST -PassThru).FailedCount" diff --git a/docker/tests/Templates/fxdependent-ubuntu16.04/Dockerfile b/docker/tests/Templates/fxdependent-ubuntu16.04/Dockerfile deleted file mode 100644 index aa976ddb19e..00000000000 --- a/docker/tests/Templates/fxdependent-ubuntu16.04/Dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -FROM ubuntu:xenial - -ARG PACKAGENAME -ARG PACKAGELOCATION -ARG PREVIEWSUFFIX= -ARG TESTLIST=/PowerShell/test/powershell/Modules/PackageManagement/PackageManagement.Tests.ps1,/PowerShell/test/powershell/engine/Module -ARG TESTDOWNLOADCOMMAND="git clone --recursive https://github.com/PowerShell/PowerShell.git" - -# Install dependencies -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - apt-utils \ - ca-certificates \ - apt-transport-https \ - locales \ - git \ - && apt-get clean - -# Install dotnet-runtime -ADD https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb . -RUN dpkg -i packages-microsoft-prod.deb -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - dotnet-runtime-2.1 \ - && apt-get clean - -# Setup the locale -ENV LANG en_US.UTF-8 -ENV LC_ALL $LANG -RUN locale-gen $LANG && update-locale - -# Install PowerShell package -ADD $PACKAGELOCATION/$PACKAGENAME . -RUN mkdir -p /opt/microsoft/powershell \ - && tar zxf $PACKAGENAME -C /opt/microsoft/powershell - -# Download and run tests -RUN $TESTDOWNLOADCOMMAND -RUN dotnet /opt/microsoft/powershell/pwsh.dll -c "Import-Module /PowerShell/build.psm1;\$dir='/usr/local/share/powershell/Modules';\$null=New-Item -Type Directory -Path \$dir -ErrorAction SilentlyContinue;Restore-PSPester -Destination \$dir;exit (Invoke-Pester $TESTLIST -PassThru).FailedCount" diff --git a/docker/tests/Templates/fxdependent-ubuntu18.04/Dockerfile b/docker/tests/Templates/fxdependent-ubuntu18.04/Dockerfile deleted file mode 100644 index adc9219a07d..00000000000 --- a/docker/tests/Templates/fxdependent-ubuntu18.04/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -FROM microsoft/dotnet:2.1.7-runtime-bionic - -ARG PACKAGENAME -ARG PACKAGELOCATION -ARG PREVIEWSUFFIX= -ARG TESTLIST=/PowerShell/test/powershell/Modules/PackageManagement/PackageManagement.Tests.ps1,/PowerShell/test/powershell/engine/Module -ARG TESTDOWNLOADCOMMAND="git clone --recursive https://github.com/PowerShell/PowerShell.git" - -# Install dependencies -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - apt-utils \ - ca-certificates \ - apt-transport-https \ - locales \ - git \ - && apt-get clean - -# Setup the locale -ENV LANG en_US.UTF-8 -ENV LC_ALL $LANG -RUN locale-gen $LANG && update-locale - -# Install PowerShell package -ADD $PACKAGELOCATION/$PACKAGENAME . -RUN mkdir -p /opt/microsoft/powershell \ - && tar zxf $PACKAGENAME -C /opt/microsoft/powershell - -# Download and run tests -RUN $TESTDOWNLOADCOMMAND -RUN dotnet /opt/microsoft/powershell/pwsh.dll -c "Import-Module /PowerShell/build.psm1;\$dir='/usr/local/share/powershell/Modules';\$null=New-Item -Type Directory -Path \$dir -ErrorAction SilentlyContinue;Restore-PSPester -Destination \$dir;exit (Invoke-Pester $TESTLIST -PassThru).FailedCount" diff --git a/docker/tests/Templates/opensuse42.3/Dockerfile b/docker/tests/Templates/opensuse42.3/Dockerfile deleted file mode 100644 index d401e611382..00000000000 --- a/docker/tests/Templates/opensuse42.3/Dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -FROM opensuse:42.3 - -ARG PACKAGENAME -ARG PACKAGELOCATION -ARG PREVIEWSUFFIX= -ARG TESTLIST=/PowerShell/test/powershell/Modules/PackageManagement/PackageManagement.Tests.ps1,/PowerShell/test/powershell/engine/Module -ARG TESTDOWNLOADCOMMAND="git clone --recursive https://github.com/PowerShell/PowerShell.git" - -ARG POWERSHELL_LINKFILE=/usr/bin/pwsh - -# Install dependencies -RUN zypper --non-interactive update --skip-interactive \ - && zypper --non-interactive install \ - glibc-locale \ - glibc-i18ndata \ - tar \ - curl \ - libunwind \ - libicu \ - openssl \ - git - -# Setup the locale -ENV LANG en_US.UTF-8 -ENV LC_ALL $LANG -RUN localedef --charmap=UTF-8 --inputfile=en_US $LANG - -RUN curl -L -o $PACKAGENAME $PACKAGELOCATION/$PACKAGENAME - -# Create the target folder where powershell will be placed -RUN mkdir -p /opt/microsoft/powershell -# Expand powershell to the target folder -RUN tar zxf $PACKAGENAME -C /opt/microsoft/powershell - -# Create the symbolic link that points to powershell -RUN ln -s /opt/microsoft/powershell/pwsh $POWERSHELL_LINKFILE - -RUN $TESTDOWNLOADCOMMAND -RUN pwsh -c "Import-Module /PowerShell/build.psm1;\$dir='/usr/local/share/powershell/Modules';\$null=New-Item -Type Directory -Path \$dir -ErrorAction SilentlyContinue;Restore-PSPester -Destination \$dir;exit (Invoke-Pester $TESTLIST -PassThru).FailedCount" diff --git a/docker/tests/Templates/ubuntu16.04/Dockerfile b/docker/tests/Templates/ubuntu16.04/Dockerfile deleted file mode 100644 index 7439eda5a7d..00000000000 --- a/docker/tests/Templates/ubuntu16.04/Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -FROM ubuntu:xenial - -ARG PACKAGENAME -ARG PACKAGELOCATION -ARG PREVIEWSUFFIX= -ARG TESTLIST=/PowerShell/test/powershell/Modules/PackageManagement/PackageManagement.Tests.ps1,/PowerShell/test/powershell/engine/Module -ARG TESTDOWNLOADCOMMAND="git clone --recursive https://github.com/PowerShell/PowerShell.git" - -# Install dependencies -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - apt-utils \ - ca-certificates \ - curl \ - apt-transport-https \ - locales \ - git \ - && apt-get clean - -# Setup the locale -ENV LANG en_US.UTF-8 -ENV LC_ALL $LANG -RUN locale-gen $LANG && update-locale - -RUN curl -L -o $PACKAGENAME $PACKAGELOCATION/$PACKAGENAME -RUN dpkg -i $PACKAGENAME || : -RUN apt-get install -y -f --no-install-recommends -RUN $TESTDOWNLOADCOMMAND -RUN pwsh$PREVIEWSUFFIX -c "Import-Module /PowerShell/build.psm1;\$dir='/usr/local/share/powershell/Modules';\$null=New-Item -Type Directory -Path \$dir -ErrorAction SilentlyContinue;Restore-PSPester -Destination \$dir;exit (Invoke-Pester $TESTLIST -PassThru).FailedCount" diff --git a/docker/tests/Templates/ubuntu18.04/Dockerfile b/docker/tests/Templates/ubuntu18.04/Dockerfile deleted file mode 100644 index 6c7a960130d..00000000000 --- a/docker/tests/Templates/ubuntu18.04/Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -FROM ubuntu:bionic - -ARG PACKAGENAME -ARG PACKAGELOCATION -ARG PREVIEWSUFFIX= -ARG TESTLIST=/PowerShell/test/powershell/Modules/PackageManagement/PackageManagement.Tests.ps1,/PowerShell/test/powershell/engine/Module -ARG TESTDOWNLOADCOMMAND="git clone --recursive https://github.com/PowerShell/PowerShell.git" - -# Install dependencies -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - apt-utils \ - ca-certificates \ - curl \ - apt-transport-https \ - locales \ - git \ - && apt-get clean - -# Setup the locale -ENV LANG en_US.UTF-8 -ENV LC_ALL $LANG -RUN locale-gen $LANG && update-locale - -RUN curl -L -o $PACKAGENAME $PACKAGELOCATION/$PACKAGENAME -RUN dpkg -i $PACKAGENAME || : -RUN apt-get install -y -f --no-install-recommends -RUN $TESTDOWNLOADCOMMAND -RUN pwsh$PREVIEWSUFFIX -c "Import-Module /PowerShell/build.psm1;\$dir='/usr/local/share/powershell/Modules';\$null=New-Item -Type Directory -Path \$dir -ErrorAction SilentlyContinue;Restore-PSPester -Destination \$dir;exit (Invoke-Pester $TESTLIST -PassThru).FailedCount" diff --git a/docker/tests/containerTestCommon.psm1 b/docker/tests/containerTestCommon.psm1 deleted file mode 100644 index c6b540abd77..00000000000 --- a/docker/tests/containerTestCommon.psm1 +++ /dev/null @@ -1,370 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -$script:forcePull = $true -# Get docker Engine OS -function Get-DockerEngineOs -{ - docker info --format '{{ .OperatingSystem }}' -} - -# Call Docker with appropriate result checksfunction Invoke-Docker -function Invoke-Docker -{ - param( - [Parameter(Mandatory=$true)] - [string[]] - $Command, - [ValidateSet("error","warning",'ignore')] - $FailureAction = 'error', - - [Parameter(Mandatory=$true)] - [string[]] - $Params, - - [switch] - $PassThru, - [switch] - $SuppressHostOutput - ) - - $ErrorActionPreference = 'Continue' - - # Log how we are running docker for troubleshooting issues - Write-Verbose "Running docker $command $params" -Verbose - if($SuppressHostOutput.IsPresent) - { - $result = docker $command $params 2>&1 - } - else - { - &'docker' $command $params 2>&1 | Tee-Object -Variable result -ErrorAction SilentlyContinue | Out-String -Stream -ErrorAction SilentlyContinue | Write-Host -ErrorAction SilentlyContinue - } - - $dockerExitCode = $LASTEXITCODE - if($PassThru.IsPresent) - { - Write-Verbose "passing through docker result$($result.length)..." -Verbose - return $result - } - elseif($dockerExitCode -ne 0 -and $FailureAction -eq 'error') - { - Write-Error "docker $command failed with: $result" -ErrorAction Stop - return $false - } - elseif($dockerExitCode -ne 0 -and $FailureAction -eq 'warning') - { - Write-Warning "docker $command failed with: $result" - return $false - } - elseif($dockerExitCode -ne 0) - { - return $false - } - - return $true -} - -# Return a list of Linux Container Test Cases -function Get-LinuxContainer -{ - foreach($os in 'centos7','ubuntu16.04') - { - Write-Output @{ - Name = $os - Path = "$PSScriptRoot/../release/$os" - } - } -} - -# Return a list of Windows Container Test Cases -function Get-WindowsContainer -{ - foreach($os in 'windowsservercore','nanoserver') - { - Write-Output @{ - Name = $os - Path = "$PSScriptRoot/../release/$os" - } - } -} - -$script:repoName = 'microsoft/powershell' -function Get-RepoName -{ - return $script:repoName -} - -function Set-RepoName -{ - param([string]$RepoName) - - $script:repoName = $RepoName - $script:forcePull = $false -} - -function Test-SkipWindows -{ - [bool] $canRunWindows = (Get-DockerEngineOs) -like 'Windows*' - return ($IsLinux -or $IsMacOS -or !$canRunWindows) -} - -function Test-SkipLinux -{ - $os = Get-DockerEngineOs - - switch -wildcard ($os) - { - '*Linux*' { - return $false - } - '*Mac' { - return $false - } - # Docker for Windows means we are running the linux kernel - 'Docker for Windows' { - return $false - } - 'Windows*' { - return $true - } - default { - throw "Unknown docker os '$os'" - } - } -} - -function Get-TestContext -{ - param( - [ValidateSet('Linux','Windows','macOS')] - [string]$Type - ) - - $resultFileName = 'results.xml' - $logFileName = 'results.log' - $containerTestDrive = '/test' - - # Return a windows context if the Context in Windows *AND* - # the current system is windows, otherwise Join-path will fail. - if($Type -eq 'Windows' -and $IsWindows) - { - $ContainerTestDrive = 'C:\test' - } - $resolvedTestDrive = (Resolve-Path "Testdrive:\").providerPath - - return @{ - ResolvedTestDrive = $resolvedTestDrive - ResolvedXmlPath = Join-Path $resolvedTestDrive -ChildPath $resultFileName - ResolvedLogPath = Join-Path $resolvedTestDrive -ChildPath $logFileName - ContainerTestDrive = $ContainerTestDrive - ContainerXmlPath = Join-Path $containerTestDrive -ChildPath $resultFileName - ContainerLogPath = Join-Path $containerTestDrive -ChildPath $logFileName - Type = $Type - ForcePull = $script:forcePull - } -} - -function Get-ContainerPowerShellVersion -{ - param( - [HashTable] $TestContext, - [string] $RepoName, - [string] $Name - ) - - $imageTag = "${script:repoName}:${Name}" - - if($TestContext.ForcePull) - { - $null=Invoke-Docker -Command 'image', 'pull' -Params $imageTag -SuppressHostOutput - } - - $runParams = @() - $localVolumeName = $testContext.resolvedTestDrive - $runParams += '--rm' - if($TestContext.Type -ne 'Windows' -and $IsWindows) - { - # use a container volume on windows because host volumes are not automatic - $volumeName = "test-volume-" + (Get-Random -Minimum 100 -Maximum 999) - - # using alpine because it's tiny - $null=Invoke-Docker -Command create -Params '-v', '/test', '--name', $volumeName, 'alpine' -SuppressHostOutput - $runParams += '--volumes-from' - $runParams += $volumeName - } - else { - $runParams += '-v' - $runParams += "${localVolumeName}:$($testContext.containerTestDrive)" - } - - $runParams += $imageTag - $runParams += 'pwsh' - $runParams += '-c' - $runParams += ('$PSVersionTable.PSVersion.ToString() | out-string | out-file -encoding ascii -FilePath '+$testContext.containerLogPath) - - $null = Invoke-Docker -Command run -Params $runParams -SuppressHostOutput - if($TestContext.Type -ne 'Windows' -and $IsWindows) - { - $null = Invoke-Docker -Command cp -Params "${volumeName}:$($testContext.containerLogPath)", $TestContext.ResolvedLogPath - $null = Invoke-Docker -Command container, rm -Params $volumeName, '--force' -SuppressHostOutput - } - return (Get-Content -Encoding Ascii $testContext.resolvedLogPath)[0] -} - -# Function defines a config mapping for testing Preview packages. -# The list of supported OS for each release can be found here: -# https://github.com/PowerShell/PowerShell-Docs/blob/staging/reference/docs-conceptual/PowerShell-Core-Support.md#supported-platforms -function Get-DefaultPreviewConfigForPackageValidation -{ - # format: = - @{ 'centos7'='rhel.7'; - 'debian.9'='debian.9'; - 'fedora28'='rhel.7'; - 'opensuse42.3'='linux-x64.tar.gz'; - 'ubuntu16.04'='ubuntu.16.04'; - 'ubuntu18.04'='ubuntu.18.04'; - 'fxdependent-centos7'='linux-x64-fxdependent.tar.gz'; - 'fxdependent-debian.9'='linux-x64-fxdependent.tar.gz'; - 'fxdependent-fedora28'='linux-x64-fxdependent.tar.gz'; - 'fxdependent-opensuse42.3'='linux-x64-fxdependent.tar.gz'; - 'fxdependent-ubuntu16.04'='linux-x64-fxdependent.tar.gz'; - 'fxdependent-ubuntu18.04'='linux-x64-fxdependent.tar.gz'; - 'fxdependent-dotnetsdk-latest'='linux-x64-fxd-dotnetsdk.tar.gz' - } -} - -# Function defines a config mapping for testing Stable packages. -# The list of supported OS for each release can be found here: -# https://github.com/PowerShell/PowerShell-Docs/blob/staging/reference/docs-conceptual/PowerShell-Core-Support.md#supported-platforms -function Get-DefaultStableConfigForPackageValidation -{ - # format: = - @{ 'centos7'='rhel.7'; - 'debian.9'='debian.9'; - 'opensuse42.3'='linux-x64.tar.gz'; - 'ubuntu16.04'='ubuntu.16.04'; - 'fxdependent-centos7'='linux-x64-fxdependent.tar.gz'; - 'fxdependent-debian.9'='linux-x64-fxdependent.tar.gz'; - 'fxdependent-opensuse42.3'='linux-x64-fxdependent.tar.gz'; - 'fxdependent-ubuntu16.04'='linux-x64-fxdependent.tar.gz' - } -} - -# Returns a list of files in a specified Azure container. -function Get-PackageNamesOnAzureBlob -{ - param( - [string] - $ContainerUrl, - - # $SAS (shared access signature) param should include beginning '?' and trailing '&' - [string] - $SAS - ) - - - $response = Invoke-RestMethod -Method Get -Uri $($ContainerUrl + $SAS + 'restype=container&comp=list') - - $xmlResponce = [xml]$response.Substring($response.IndexOf(' - + - - diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimAsyncOperation.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimAsyncOperation.cs index 49f39c8c019..e5f31f385fc 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimAsyncOperation.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimAsyncOperation.cs @@ -206,10 +206,7 @@ protected void AddCimSessionProxy(CimSessionProxy sessionproxy) { lock (cimSessionProxyCacheLock) { - if (this.cimSessionProxyCache == null) - { - this.cimSessionProxyCache = new List(); - } + this.cimSessionProxyCache ??= new List(); if (!this.cimSessionProxyCache.Contains(sessionproxy)) { @@ -531,10 +528,7 @@ private void Cleanup() } this.moreActionEvent.Dispose(); - if (this.ackedEvent != null) - { - this.ackedEvent.Dispose(); - } + this.ackedEvent?.Dispose(); DebugHelper.WriteLog("Cleanup complete.", 2); } diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimBaseAction.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimBaseAction.cs index e3e487a9533..4702e47e2f2 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimBaseAction.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimBaseAction.cs @@ -168,10 +168,7 @@ protected virtual void Dispose(bool disposing) if (disposing) { // Dispose managed resources. - if (this.completeEvent != null) - { - this.completeEvent.Dispose(); - } + this.completeEvent?.Dispose(); } // Call the appropriate methods to clean up diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimCommandBase.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimCommandBase.cs index 4e9bf4f296e..89f7478b513 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimCommandBase.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimCommandBase.cs @@ -390,10 +390,7 @@ internal string GetParameterSet() } // Looking for default parameter set - if (boundParameterSetName == null) - { - boundParameterSetName = defaultParameterSetName; - } + boundParameterSetName ??= defaultParameterSetName; // throw if still can not find the parameter set name if (boundParameterSetName == null) @@ -473,10 +470,7 @@ internal void SetParameter(object value, string parameterName) return; } - if (this.parameterBinder != null) - { - this.parameterBinder.SetParameter(parameterName, this.AtBeginProcess); - } + this.parameterBinder?.SetParameter(parameterName, this.AtBeginProcess); } #endregion @@ -579,10 +573,7 @@ protected void Dispose(bool disposing) protected virtual void DisposeInternal() { // Dispose managed resources. - if (this.operation != null) - { - this.operation.Dispose(); - } + this.operation?.Dispose(); } #endregion diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetCimClass.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetCimClass.cs index 728ce6b8c8e..c775325094b 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetCimClass.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetCimClass.cs @@ -165,6 +165,7 @@ private static void SetSessionProxyProperties( GetCimClassCommand cmdlet) { proxy.OperationTimeout = cmdlet.OperationTimeoutSec; + proxy.Amended = cmdlet.Amended; } /// diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSessionProxy.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSessionProxy.cs index 9321c86ec77..e40ebb40930 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSessionProxy.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSessionProxy.cs @@ -539,6 +539,26 @@ private void CreateSetSession( #region set operation options + /// + /// Gets or sets a value indicating whether to retrieve localized information for the CIM class. + /// + public bool Amended + { + get => OperationOptions.Flags.HasFlag(CimOperationFlags.LocalizedQualifiers); + + set + { + if (value) + { + OperationOptions.Flags |= CimOperationFlags.LocalizedQualifiers; + } + else + { + OperationOptions.Flags &= ~CimOperationFlags.LocalizedQualifiers; + } + } + } + /// /// Set timeout value (seconds) of the operation. /// @@ -666,9 +686,9 @@ private void InitOption(CimOperationOptions operOptions) { this.OperationOptions = new CimOperationOptions(operOptions); } - else if (this.OperationOptions == null) + else { - this.OperationOptions = new CimOperationOptions(); + this.OperationOptions ??= new CimOperationOptions(); } this.EnableMethodResultStreaming = true; diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimAssociatedInstanceCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimAssociatedInstanceCommand.cs index 90acd1c6c0e..6c4d0c94d2b 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimAssociatedInstanceCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimAssociatedInstanceCommand.cs @@ -232,8 +232,7 @@ protected override void ProcessRecord() protected override void EndProcessing() { CimGetAssociatedInstance operation = this.GetOperationAgent(); - if (operation != null) - operation.ProcessRemainActions(this.CmdletOperation); + operation?.ProcessRemainActions(this.CmdletOperation); } #endregion diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimClassCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimClassCommand.cs index adc669ab645..1bededc485f 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimClassCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimClassCommand.cs @@ -43,6 +43,12 @@ public GetCimClassCommand() #region parameters + /// + /// Gets or sets flag to retrieve a localized data for WMI class. + /// + [Parameter] + public SwitchParameter Amended { get; set; } + /// /// /// The following is the definition of the input parameter "ClassName". @@ -196,10 +202,7 @@ protected override void ProcessRecord() protected override void EndProcessing() { CimGetCimClass cimGetCimClass = this.GetOperationAgent(); - if (cimGetCimClass != null) - { - cimGetCimClass.ProcessRemainActions(this.CmdletOperation); - } + cimGetCimClass?.ProcessRemainActions(this.CmdletOperation); } #endregion diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimInstanceCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimInstanceCommand.cs index d61a9363922..65eeae8e450 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimInstanceCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimInstanceCommand.cs @@ -491,10 +491,7 @@ protected override void ProcessRecord() protected override void EndProcessing() { CimGetInstance cimGetInstance = this.GetOperationAgent(); - if (cimGetInstance != null) - { - cimGetInstance.ProcessRemainActions(this.CmdletOperation); - } + cimGetInstance?.ProcessRemainActions(this.CmdletOperation); } #endregion diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/InvokeCimMethodCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/InvokeCimMethodCommand.cs index 26e1d8eaf5f..e3bc6f293b6 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/InvokeCimMethodCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/InvokeCimMethodCommand.cs @@ -408,10 +408,7 @@ protected override void ProcessRecord() protected override void EndProcessing() { CimInvokeCimMethod cimInvokeMethod = this.GetOperationAgent(); - if (cimInvokeMethod != null) - { - cimInvokeMethod.ProcessRemainActions(this.CmdletOperation); - } + cimInvokeMethod?.ProcessRemainActions(this.CmdletOperation); } #endregion diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimInstanceCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimInstanceCommand.cs index 53d7c28b924..5843f25a26b 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimInstanceCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimInstanceCommand.cs @@ -377,10 +377,7 @@ protected override void ProcessRecord() protected override void EndProcessing() { CimNewCimInstance cimNewCimInstance = this.GetOperationAgent(); - if (cimNewCimInstance != null) - { - cimNewCimInstance.ProcessRemainActions(this.CmdletOperation); - } + cimNewCimInstance?.ProcessRemainActions(this.CmdletOperation); } #endregion diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimSessionCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimSessionCommand.cs index 9957372d592..f048a22a9f6 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimSessionCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimSessionCommand.cs @@ -334,10 +334,7 @@ protected override void DisposeInternal() base.DisposeInternal(); // Dispose managed resources. - if (this.cimNewSession != null) - { - this.cimNewSession.Dispose(); - } + this.cimNewSession?.Dispose(); } #endregion } diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/RegisterCimIndicationCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/RegisterCimIndicationCommand.cs index d22644ececd..d5dc44a31d4 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/RegisterCimIndicationCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/RegisterCimIndicationCommand.cs @@ -227,10 +227,7 @@ protected override object GetSourceObject() break; } - if (watcher != null) - { - watcher.SetCmdlet(this); - } + watcher?.SetCmdlet(this); return watcher; } @@ -275,10 +272,7 @@ private static void newSubscriber_Unsubscribed( DebugHelper.WriteLogEx(); CimIndicationWatcher watcher = sender as CimIndicationWatcher; - if (watcher != null) - { - watcher.Stop(); - } + watcher?.Stop(); } #region private members diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/RemoveCimInstanceCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/RemoveCimInstanceCommand.cs index 273311485ce..5ac8d129367 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/RemoveCimInstanceCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/RemoveCimInstanceCommand.cs @@ -276,10 +276,7 @@ protected override void ProcessRecord() protected override void EndProcessing() { CimRemoveCimInstance cimRemoveInstance = this.GetOperationAgent(); - if (cimRemoveInstance != null) - { - cimRemoveInstance.ProcessRemainActions(this.CmdletOperation); - } + cimRemoveInstance?.ProcessRemainActions(this.CmdletOperation); } #endregion diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/SetCimInstanceCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/SetCimInstanceCommand.cs index de6857db5eb..d190e5fafba 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/SetCimInstanceCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/SetCimInstanceCommand.cs @@ -325,10 +325,7 @@ protected override void ProcessRecord() protected override void EndProcessing() { CimSetCimInstance cimSetCimInstance = this.GetOperationAgent(); - if (cimSetCimInstance != null) - { - cimSetCimInstance.ProcessRemainActions(this.CmdletOperation); - } + cimSetCimInstance?.ProcessRemainActions(this.CmdletOperation); } #endregion diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/KeyboardHelp.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/KeyboardHelp.cs index 0d174fb25ed..386b33996c1 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/Common/KeyboardHelp.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/KeyboardHelp.cs @@ -106,7 +106,7 @@ public static FocusNavigationDirection GetNavigationDirection(DependencyObject e /// /// Determines if the control key is pressed. /// - /// True if a control is is pressed. + /// True if a control is pressed. public static bool IsControlPressed() { if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizerItem.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizerItem.cs index 469ed5aec77..24ff69bc35b 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizerItem.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizerItem.cs @@ -153,7 +153,7 @@ private void RevertTextAndChangeFromEditToDisplayMode() private void ChangeFromEditToDisplayMode() { // NOTE : This is to resolve a race condition where clicking - // on the rename button causes the the edit box to change and + // on the rename button causes the edit box to change and // then have re-toggle. DependencyObject d = Mouse.DirectlyOver as DependencyObject; if (d == null || !(this.renameButton.IsAncestorOf(d) && Mouse.LeftButton == MouseButtonState.Pressed)) diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRuleTemplateSelector.cs b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRuleTemplateSelector.cs index fc40c3c5768..0381ef0e63c 100644 --- a/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRuleTemplateSelector.cs +++ b/src/Microsoft.Management.UI.Internal/ManagementList/FilterProviders/FilterRuleTemplateSelector.cs @@ -26,7 +26,7 @@ public IDictionary TemplateDictionary } /// - /// Selects a template based upon the type of the item and and the + /// Selects a template based upon the type of the item and the /// corresponding template that is registered in the TemplateDictionary. /// /// diff --git a/src/Microsoft.Management.UI.Internal/commandHelpers/HelpWindowHelper.cs b/src/Microsoft.Management.UI.Internal/commandHelpers/HelpWindowHelper.cs index efed820e71c..ddb9ab191ae 100644 --- a/src/Microsoft.Management.UI.Internal/commandHelpers/HelpWindowHelper.cs +++ b/src/Microsoft.Management.UI.Internal/commandHelpers/HelpWindowHelper.cs @@ -13,7 +13,7 @@ namespace Microsoft.PowerShell.Commands.Internal { /// - /// Implements thw WPF window part of the the ShowWindow option of get-help. + /// Implements thw WPF window part of the ShowWindow option of get-help. /// internal static class HelpWindowHelper { diff --git a/src/Microsoft.Management.UI.Internal/commandHelpers/OutGridView.cs b/src/Microsoft.Management.UI.Internal/commandHelpers/OutGridView.cs index f38a8a70945..81621624992 100644 --- a/src/Microsoft.Management.UI.Internal/commandHelpers/OutGridView.cs +++ b/src/Microsoft.Management.UI.Internal/commandHelpers/OutGridView.cs @@ -6,6 +6,7 @@ using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Management.Automation; +using System.Management.Automation.Internal; using System.Threading; using System.Windows; using System.Windows.Automation; @@ -496,6 +497,16 @@ private void AddItem(PSObject value) { try { + // Remove any potential ANSI decoration + foreach (var property in value.Properties) + { + if (property.Value is string str) + { + StringDecorated decoratedString = new StringDecorated(str); + property.Value = decoratedString.ToString(OutputRendering.PlainText); + } + } + this.listItems.Add(value); } catch (Exception e) diff --git a/src/Microsoft.Management.UI.Internal/commandHelpers/ShowCommandHelper.cs b/src/Microsoft.Management.UI.Internal/commandHelpers/ShowCommandHelper.cs index 873f15e2c41..2268edb99f5 100644 --- a/src/Microsoft.Management.UI.Internal/commandHelpers/ShowCommandHelper.cs +++ b/src/Microsoft.Management.UI.Internal/commandHelpers/ShowCommandHelper.cs @@ -1200,7 +1200,7 @@ private void Buttons_CopyClick(object sender, RoutedEventArgs e) } /// - /// Sets a succesfull dialog result and then closes the window. + /// Sets a successful dialog result and then closes the window. /// /// Event sender. /// Event arguments. diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/NewWinEventCommand.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/NewWinEventCommand.cs index cb74c292a3e..b0b102870c4 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/NewWinEventCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/NewWinEventCommand.cs @@ -317,8 +317,7 @@ protected override void ProcessRecord() /// protected override void EndProcessing() { - if (_providerMetadata != null) - _providerMetadata.Dispose(); + _providerMetadata?.Dispose(); base.EndProcessing(); } diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/PdhHelper.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/PdhHelper.cs index 97eff22db0d..b761aa6e30b 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/PdhHelper.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/PdhHelper.cs @@ -267,28 +267,65 @@ private struct PDH_TIME_INFO // We only need dwType and lDefaultScale fields from this structure. // We access those fields directly. The struct is here for reference only. // - [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] - private struct PDH_COUNTER_INFO + [StructLayout(LayoutKind.Sequential)] + private unsafe struct PDH_COUNTER_INFO { - [FieldOffset(0)] public UInt32 dwLength; - [FieldOffset(4)] public UInt32 dwType; - [FieldOffset(8)] public UInt32 CVersion; - [FieldOffset(12)] public UInt32 CStatus; - [FieldOffset(16)] public UInt32 lScale; - [FieldOffset(20)] public UInt32 lDefaultScale; - [FieldOffset(24)] public IntPtr dwUserData; - [FieldOffset(32)] public IntPtr dwQueryUserData; - [FieldOffset(40)] public string szFullPath; - - [FieldOffset(48)] public string szMachineName; - [FieldOffset(56)] public string szObjectName; - [FieldOffset(64)] public string szInstanceName; - [FieldOffset(72)] public string szParentInstance; - [FieldOffset(80)] public UInt32 dwInstanceIndex; - [FieldOffset(88)] public string szCounterName; - - [FieldOffset(96)] public string szExplainText; - [FieldOffset(104)] public IntPtr DataBuffer; + public uint Length; + public uint Type; + public uint CVersion; + public uint CStatus; + public int Scale; + public int DefaultScale; + public ulong UserData; + public ulong QueryUserData; + public ushort* FullPath; + public _Anonymous_e__Union Anonymous; + public ushort* ExplainText; + public fixed uint DataBuffer[1]; + + [StructLayout(LayoutKind.Explicit)] + internal struct _Anonymous_e__Union + { + [FieldOffset(0)] + public PDH_DATA_ITEM_PATH_ELEMENTS_blittable DataItemPath; + + [FieldOffset(0)] + public PDH_COUNTER_PATH_ELEMENTS_blittable CounterPath; + + [FieldOffset(0)] + public _Anonymous_e__Struct Anonymous; + + [StructLayout(LayoutKind.Sequential)] + internal struct PDH_DATA_ITEM_PATH_ELEMENTS_blittable + { + public ushort* MachineName; + public Guid ObjectGUID; + public uint ItemId; + public ushort* InstanceName; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct PDH_COUNTER_PATH_ELEMENTS_blittable + { + public ushort* MachineName; + public ushort* ObjectName; + public ushort* InstanceName; + public ushort* ParentInstance; + public uint InstanceIndex; + public ushort* CounterName; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct _Anonymous_e__Struct + { + public ushort* MachineName; + public ushort* ObjectName; + public ushort* InstanceName; + public ushort* ParentInstance; + public uint InstanceIndex; + public ushort* CounterName; + } + } } [DllImport("pdh.dll", CharSet = CharSet.Unicode)] @@ -476,8 +513,8 @@ private static uint GetCounterInfoPlus(IntPtr hCounter, out UInt32 counterType, if (res == PdhResults.PDH_CSTATUS_VALID_DATA && bufCounterInfo != IntPtr.Zero) { PDH_COUNTER_INFO pdhCounterInfo = (PDH_COUNTER_INFO)Marshal.PtrToStructure(bufCounterInfo, typeof(PDH_COUNTER_INFO)); - counterType = pdhCounterInfo.dwType; - defaultScale = pdhCounterInfo.lDefaultScale; + counterType = pdhCounterInfo.Type; + defaultScale = (uint)pdhCounterInfo.DefaultScale; } } finally diff --git a/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj b/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj index 05b7b4e7472..06bd7872cbe 100644 --- a/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj +++ b/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj @@ -47,7 +47,7 @@ - + diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/SessionBasedWrapper.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/SessionBasedWrapper.cs index 0ec77a18d26..cca50c4bb91 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/SessionBasedWrapper.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/SessionBasedWrapper.cs @@ -683,10 +683,7 @@ public override void EndProcessing() public override void StopProcessing() { Job jobToStop = _parentJob; - if (jobToStop != null) - { - jobToStop.StopJob(); - } + jobToStop?.StopJob(); base.StopProcessing(); } diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimChildJobBase.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimChildJobBase.cs index cb4786f005e..8a973c7af64 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimChildJobBase.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimChildJobBase.cs @@ -315,10 +315,7 @@ internal override void StartJob() this.ExceptionSafeWrapper(delegate { IObservable observable = this.GetCimOperation(); - if (observable != null) - { - observable.Subscribe(this); - } + observable?.Subscribe(this); }); }); } @@ -522,10 +519,7 @@ internal CimOperationOptions CreateOperationOptions() } CimCustomOptionsDictionary jobSpecificCustomOptions = this.GetJobSpecificCustomOptions(); - if (jobSpecificCustomOptions != null) - { - jobSpecificCustomOptions.Apply(operationOptions, CimSensitiveValueConverter); - } + jobSpecificCustomOptions?.Apply(operationOptions, CimSensitiveValueConverter); return operationOptions; } @@ -1053,10 +1047,7 @@ internal override void WriteObject(object outputObject) if (this.JobContext.ShowComputerName) { - if (pso == null) - { - pso = PSObject.AsPSObject(outputObject); - } + pso ??= PSObject.AsPSObject(outputObject); AddShowComputerNameMarker(pso); if (cimInstance == null) diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimWrapper.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimWrapper.cs index 7052b16c768..c812778456a 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimWrapper.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimWrapper.cs @@ -107,15 +107,12 @@ internal CimCmdletDefinitionContext CmdletDefinitionContext { get { - if (_cmdletDefinitionContext == null) - { - _cmdletDefinitionContext = new CimCmdletDefinitionContext( - this.ClassName, - this.ClassVersion, - this.ModuleVersion, - this.Cmdlet.CommandInfo.CommandMetadata.SupportsShouldProcess, - this.PrivateData); - } + _cmdletDefinitionContext ??= new CimCmdletDefinitionContext( + this.ClassName, + this.ClassVersion, + this.ModuleVersion, + this.Cmdlet.CommandInfo.CommandMetadata.SupportsShouldProcess, + this.PrivateData); return _cmdletDefinitionContext; } diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Computer.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Computer.cs index 73c2f309508..e1b06c0bc94 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Computer.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Computer.cs @@ -306,7 +306,7 @@ public short Delay ComputerName = $computerName ScriptBlock = { $true } - SessionOption = NewPSSessionOption -NoMachineProfile + SessionOption = New-PSSessionOption -NoMachineProfile ErrorAction = 'SilentlyContinue' } @@ -393,17 +393,10 @@ public void Dispose(bool disposing) { if (disposing) { - if (_timer != null) - { - _timer.Dispose(); - } - + _timer?.Dispose(); _waitHandler.Dispose(); _cancel.Dispose(); - if (_powershell != null) - { - _powershell.Dispose(); - } + _powershell?.Dispose(); } } @@ -1098,10 +1091,7 @@ protected override void StopProcessing() _cancel.Cancel(); _waitHandler.Set(); - if (_timer != null) - { - _timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); - } + _timer?.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); if (_powershell != null) { @@ -1278,6 +1268,7 @@ private void ProcessWSManProtocol(object[] flags) /// [Cmdlet(VerbsCommon.Rename, "Computer", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097054", RemotingCapability = RemotingCapability.SupportedByCommand)] + [OutputType(typeof(RenameComputerChangeInfo))] public class RenameComputerCommand : PSCmdlet { #region Private Members @@ -2230,8 +2221,7 @@ internal static string ValidateComputerName( bool isIPAddress = false; try { - IPAddress unused; - isIPAddress = IPAddress.TryParse(nameToCheck, out unused); + isIPAddress = IPAddress.TryParse(nameToCheck, out _); } catch (Exception) { diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/ComputerUnix.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/ComputerUnix.cs index fb01823790f..a11fb0270c9 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/ComputerUnix.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/ComputerUnix.cs @@ -67,7 +67,7 @@ public sealed class StopComputerCommand : CommandLineCmdletBase protected override void BeginProcessing() { var args = "-P now"; - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + if (Platform.IsMacOS) { args = "now"; } diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/ContentCommandBase.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/ContentCommandBase.cs index 0c990292b58..87c7731ed70 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/ContentCommandBase.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/ContentCommandBase.cs @@ -377,10 +377,7 @@ internal void CloseContent(List contentHolders, bool disposing) { try { - if (holder.Writer != null) - { - holder.Writer.Close(); - } + holder.Writer?.Close(); } catch (Exception e) // Catch-all OK. 3rd party callout { @@ -414,10 +411,7 @@ internal void CloseContent(List contentHolders, bool disposing) try { - if (holder.Reader != null) - { - holder.Reader.Close(); - } + holder.Reader?.Close(); } catch (Exception e) // Catch-all OK. 3rd party callout { diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Eventlog.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Eventlog.cs index 45c484744fa..89c58acbf57 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Eventlog.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Eventlog.cs @@ -1344,7 +1344,7 @@ public class RemoveEventLogCommand : PSCmdlet /// /// The following is the definition of the input parameter "RemoveSource". - /// Specifies either to remove the event log and and associated source or + /// Specifies either to remove the event log and associated source or /// source. alone. /// When this parameter is not specified, the cmdlet uses Delete Method which /// clears the eventlog and also the source associated with it. diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetComputerInfoCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetComputerInfoCommand.cs index 499358389f4..a57756bbf75 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetComputerInfoCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetComputerInfoCommand.cs @@ -1130,11 +1130,8 @@ internal static string GetLocaleName(string locale) culture = CultureInfo.GetCultureInfo((int)localeNum); } - if (culture == null) - { - // If TryParse failed we'll try using the original string as culture name - culture = CultureInfo.GetCultureInfo(locale); - } + // If TryParse failed we'll try using the original string as culture name + culture ??= CultureInfo.GetCultureInfo(locale); } catch (Exception) { @@ -3687,7 +3684,22 @@ public enum DeviceGuardHardwareSecure /// /// Secure Memory Overwrite. /// - SecureMemoryOverwrite = 4 + SecureMemoryOverwrite = 4, + + /// + /// UEFI Code Readonly. + /// + UEFICodeReadonly = 5, + + /// + /// SMM Security Mitigations 1.0. + /// + SMMSecurityMitigations = 6, + + /// + /// Mode Based Execution Control. + /// + ModeBasedExecutionControl = 7 } /// diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetContentCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetContentCommand.cs index f689695fc38..f2e4598c1bc 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetContentCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetContentCommand.cs @@ -277,7 +277,7 @@ protected override void ProcessRecord() /// /// /// - /// true if no error occured + /// true if no error occurred /// false if there was an error /// private bool ScanForwardsForTail(in ContentHolder holder, CmdletProviderContext currentContext) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Hotfix.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Hotfix.cs index d6768a1315e..1aa94509469 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Hotfix.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Hotfix.cs @@ -169,10 +169,7 @@ protected override void ProcessRecord() /// protected override void StopProcessing() { - if (_searchProcess != null) - { - _searchProcess.Dispose(); - } + _searchProcess?.Dispose(); } #endregion Overrides @@ -227,10 +224,7 @@ public void Dispose(bool disposing) { if (disposing) { - if (_searchProcess != null) - { - _searchProcess.Dispose(); - } + _searchProcess?.Dispose(); } } diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/MovePropertyCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/MovePropertyCommand.cs index fb90ec1c502..a40cae64b9f 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/MovePropertyCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/MovePropertyCommand.cs @@ -61,10 +61,7 @@ public string[] Name set { - if (value == null) - { - value = Array.Empty(); - } + value ??= Array.Empty(); _property = value; } diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs index ef4f5b1a7b4..7cbd1eea1f3 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs @@ -938,10 +938,7 @@ private void myProcess_Exited(object sender, System.EventArgs e) { if (System.Threading.Interlocked.Decrement(ref _numberOfProcessesToWaitFor) == 0) { - if (_waitHandle != null) - { - _waitHandle.Set(); - } + _waitHandle?.Set(); } } @@ -1040,13 +1037,8 @@ protected override void EndProcessing() /// /// StopProcessing. /// - protected override void StopProcessing() - { - if (_waitHandle != null) - { - _waitHandle.Set(); - } - } + protected override void StopProcessing() => _waitHandle?.Set(); + #endregion Overrides } @@ -1916,8 +1908,12 @@ protected override void BeginProcessing() } else { - // Working Directory not specified -> Assign Current Path. - startInfo.WorkingDirectory = PathUtils.ResolveFilePath(this.SessionState.Path.CurrentFileSystemLocation.Path, this, isLiteralPath: true); + // Working Directory not specified -> Assign Current Path, but only if it still exists + var currentDirectory = PathUtils.ResolveFilePath(this.SessionState.Path.CurrentFileSystemLocation.Path, this, isLiteralPath: true); + if (Directory.Exists(currentDirectory)) + { + startInfo.WorkingDirectory = currentDirectory; + } } if (this.ParameterSetName.Equals("Default")) @@ -2079,13 +2075,7 @@ protected override void BeginProcessing() /// /// Implements ^c, after creating a process. /// - protected override void StopProcessing() - { - if (_waithandle != null) - { - _waithandle.Set(); - } - } + protected override void StopProcessing() => _waithandle?.Set(); #endregion @@ -2116,13 +2106,7 @@ private void Dispose(bool isDisposing) /// /// When Process exits the wait handle is set. /// - private void myProcess_Exited(object sender, System.EventArgs e) - { - if (_waithandle != null) - { - _waithandle.Set(); - } - } + private void myProcess_Exited(object sender, System.EventArgs e) => _waithandle?.Set(); private string ResolveFilePath(string path) { @@ -2221,15 +2205,8 @@ private void StreamClosing() { Thread.Sleep(1000); - if (_outputWriter != null) - { - _outputWriter.Dispose(); - } - - if (_errorWriter != null) - { - _errorWriter.Dispose(); - } + _outputWriter?.Dispose(); + _errorWriter?.Dispose(); } private void SetupInputOutputRedirection(Process p) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs index a089f204654..8ac17c9f616 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs @@ -1756,10 +1756,14 @@ protected override void ProcessRecord() return; } + var access = NativeMethods.SERVICE_CHANGE_CONFIG; + if (!string.IsNullOrEmpty(SecurityDescriptorSddl)) + access |= NativeMethods.WRITE_DAC | NativeMethods.WRITE_OWNER; + hService = NativeMethods.OpenServiceW( hScManager, Name, - NativeMethods.SERVICE_CHANGE_CONFIG | NativeMethods.WRITE_DAC | NativeMethods.WRITE_OWNER + access ); if (hService == IntPtr.Zero) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/TimeZoneCommands.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/TimeZoneCommands.cs index 69e8860f3ab..8fdb2c55acd 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/TimeZoneCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/TimeZoneCommands.cs @@ -17,6 +17,7 @@ namespace Microsoft.PowerShell.Commands /// [Cmdlet(VerbsCommon.Get, "TimeZone", DefaultParameterSetName = "Name", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096904")] + [OutputType(typeof(TimeZoneInfo))] [Alias("gtz")] public class GetTimeZoneCommand : PSCmdlet { @@ -121,6 +122,7 @@ protected override void ProcessRecord() SupportsShouldProcess = true, DefaultParameterSetName = "Name", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2097056")] + [OutputType(typeof(TimeZoneInfo))] [Alias("stz")] public class SetTimeZoneCommand : PSCmdlet { @@ -159,7 +161,7 @@ public class SetTimeZoneCommand : PSCmdlet #endregion Parameters /// - /// Implementation of the ProcessRecord method for Get-TimeZone. + /// Implementation of the ProcessRecord method for Set-TimeZone. /// [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "Since Name is not a parameter of this method, it confuses FXCop. It is the appropriate value for the exception.")] protected override void ProcessRecord() diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/WriteContentCommandBase.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/WriteContentCommandBase.cs index 25ecd672c4c..a2a6387e9da 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/WriteContentCommandBase.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/WriteContentCommandBase.cs @@ -95,10 +95,7 @@ protected override void ProcessRecord() // Initialize the content - if (_content == null) - { - _content = Array.Empty(); - } + _content ??= Array.Empty(); if (_pipingPaths) { diff --git a/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj b/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj index ca77b060e53..21e870bd2f1 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj +++ b/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj @@ -7,8 +7,10 @@ + - + + @@ -31,10 +33,10 @@ - - - - + + + + diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs index 48ee839e99a..00d6b11efcf 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs @@ -11,7 +11,9 @@ using System.Linq; using System.Management.Automation; using System.Management.Automation.Internal; +using System.Management.Automation.Security; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.Loader; using System.Security; using System.Text; @@ -549,8 +551,10 @@ private string GetUsingSet(Language language) /// protected override void BeginProcessing() { - // Prevent code compilation in ConstrainedLanguage mode - if (SessionState.LanguageMode == PSLanguageMode.ConstrainedLanguage) + // Prevent code compilation in ConstrainedLanguage mode, or NoLanguage mode under system lock down. + if (SessionState.LanguageMode == PSLanguageMode.ConstrainedLanguage || + (SessionState.LanguageMode == PSLanguageMode.NoLanguage && + SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce)) { ThrowTerminatingError( new ErrorRecord( @@ -682,11 +686,10 @@ private void LoadAssemblies(IEnumerable assemblies) /// private static IEnumerable InitDefaultRefAssemblies() { - // Define number of reference assemblies distributed with PowerShell. - const int maxPowershellRefAssemblies = 160; - - const int capacity = maxPowershellRefAssemblies + 1; - var defaultRefAssemblies = new List(capacity); + // Default reference assemblies consist of .NET reference assemblies and the 'S.M.A' assembly. + // Today, there are 161 .NET reference assemblies, so the needed capacity is 162, but we use 200 + // as the initial capacity to cover the possible increase of .NET reference assemblies in future. + var defaultRefAssemblies = new List(capacity: 200); foreach (string file in Directory.EnumerateFiles(s_netcoreAppRefFolder, "*.dll", SearchOption.TopDirectoryOnly)) { @@ -696,11 +699,6 @@ private static IEnumerable InitDefaultRefAssemblies // Add System.Management.Automation.dll defaultRefAssemblies.Add(MetadataReference.CreateFromFile(typeof(PSObject).Assembly.Location)); - // We want to avoid reallocating the internal array, so we assert if the list capacity has increased. - Diagnostics.Assert( - defaultRefAssemblies.Capacity <= capacity, - $"defaultRefAssemblies was resized because of insufficient initial capacity! A capacity of {defaultRefAssemblies.Count} is required."); - return defaultRefAssemblies; } @@ -894,7 +892,14 @@ private IEnumerable GetPortableExecutableReferences private void WriteTypes(Assembly assembly) { - WriteObject(assembly.GetTypes(), true); + foreach (Type type in assembly.GetTypes()) + { + // We only write out types that are not auto-generated by compiler. + if (type.GetCustomAttribute() is null) + { + WriteObject(type); + } + } } #endregion LoadAssembly diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertTo-Html.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertTo-Html.cs index e72fc825e7c..2f1e5cca314 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertTo-Html.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ConvertTo-Html.cs @@ -19,6 +19,7 @@ namespace Microsoft.PowerShell.Commands /// [Cmdlet(VerbsData.ConvertTo, "Html", DefaultParameterSetName = "Page", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096595", RemotingCapability = RemotingCapability.None)] + [OutputType(typeof(string))] public sealed class ConvertToHtmlCommand : PSCmdlet { @@ -343,10 +344,7 @@ private List ProcessParameter(object[] properties) TerminatingErrorContext invocationContext = new(this); ParameterProcessor processor = new(new ConvertHTMLExpressionParameterDefinition()); - if (properties == null) - { - properties = new object[] { "*" }; - } + properties ??= new object[] { "*" }; return processor.ProcessParameters(properties, invocationContext); } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs index 19ac076deaf..eb3ebacff4a 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CsvCommands.cs @@ -429,10 +429,7 @@ private void CleanUp() _readOnlyFileInfo.Attributes |= FileAttributes.ReadOnly; } - if (_helper != null) - { - _helper.Dispose(); - } + _helper?.Dispose(); } private void ReconcilePreexistingPropertyNames() diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CustomSerialization.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CustomSerialization.cs index 6ad8b02a558..5f810201e71 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CustomSerialization.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/CustomSerialization.cs @@ -170,10 +170,7 @@ internal void DoneAsStream() internal void Stop() { CustomInternalSerializer serializer = _serializer; - if (serializer != null) - { - serializer.Stop(); - } + serializer?.Stop(); } #endregion diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/DebugRunspaceCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/DebugRunspaceCommand.cs index f6ba764662f..fc257810d55 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/DebugRunspaceCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/DebugRunspaceCommand.cs @@ -236,10 +236,7 @@ protected override void StopProcessing() // Unblock the data collection. PSDataCollection debugCollection = _debugBlockingCollection; - if (debugCollection != null) - { - debugCollection.Complete(); - } + debugCollection?.Complete(); // Unblock any new command wait. _newRunningScriptEvent.Set(); @@ -334,9 +331,8 @@ private void HostWriteLine(string line) private void AddDataEventHandlers() { // Create new collection objects. - if (_debugBlockingCollection != null) { _debugBlockingCollection.Dispose(); } - - if (_debugAccumulateCollection != null) { _debugAccumulateCollection.Dispose(); } + _debugBlockingCollection?.Dispose(); + _debugAccumulateCollection?.Dispose(); _debugBlockingCollection = new PSDataCollection(); _debugBlockingCollection.BlockingEnumerator = true; diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs index c37d83ecf84..e4d5b5d6b27 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs @@ -275,10 +275,7 @@ protected void SetDebugPreferenceHelper(string processName, string[] appDomainNa { if (!string.IsNullOrEmpty(currentAppDomainName)) { - if (appDomainNames == null) - { - appDomainNames = new List(); - } + appDomainNames ??= new List(); appDomainNames.Add(currentAppDomainName.ToLowerInvariant()); } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ExportAliasCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ExportAliasCommand.cs index e01d8c28e0c..fcf282d09c8 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ExportAliasCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ExportAliasCommand.cs @@ -291,8 +291,7 @@ protected override void EndProcessing() line = GetAliasLine(alias, "set-alias -Name:\"{0}\" -Value:\"{1}\" -Description:\"{2}\" -Option:\"{3}\""); } - if (writer != null) - writer.WriteLine(line); + writer?.WriteLine(line); if (PassThru) { @@ -302,8 +301,7 @@ protected override void EndProcessing() } finally { - if (writer != null) - writer.Dispose(); + writer?.Dispose(); // reset the read-only attribute if (readOnlyFileInfo != null) readOnlyFileInfo.Attributes |= FileAttributes.ReadOnly; diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs index e606e58e9e4..5f231780055 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs @@ -145,7 +145,7 @@ protected override void EndProcessing() // The pipeline will be blocked while we don't return if (this.Wait || this.OutputMode != OutputModeOption.None) { - _windowProxy.BlockUntillClosed(); + _windowProxy.BlockUntilClosed(); } // Output selected items to pipeline. diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutWindowProxy.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutWindowProxy.cs index 3f22caf1e7b..c7f2c622c08 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutWindowProxy.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutWindowProxy.cs @@ -230,13 +230,7 @@ internal void ShowWindow() } } - internal void BlockUntillClosed() - { - if (_closedEvent != null) - { - _closedEvent.WaitOne(); - } - } + internal void BlockUntilClosed() => _closedEvent?.WaitOne(); /// /// Implements IDisposable logic. diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/TableView.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/TableView.cs index 216d9121d54..25b18dfd80e 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/TableView.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/TableView.cs @@ -72,11 +72,8 @@ internal HeaderInfo GenerateHeaderInfo(PSObject input, TableControlBody tableBod FieldPropertyToken fpt = token as FieldPropertyToken; if (fpt != null) { - if (displayName == null) - { - // Database does not provide a label(DisplayName) for the current property, use the expression value instead. - displayName = fpt.expression.expressionValue; - } + // If Database does not provide a label(DisplayName) for the current property, use the expression value instead. + displayName ??= fpt.expression.expressionValue; if (fpt.expression.isScriptBlock) { @@ -170,10 +167,7 @@ internal HeaderInfo GenerateHeaderInfo(PSObject input, OutGridViewCommand parent propertyName = (string)key; } - if (propertyName == null) - { - propertyName = association.ResolvedExpression.ToString(); - } + propertyName ??= association.ResolvedExpression.ToString(); ColumnInfo columnInfo = new OriginalColumnInfo(propertyName, propertyName, propertyName, parentCmdlet); @@ -233,10 +227,7 @@ private List GetActiveTableRowDefinition(TableControlBod } } - if (matchingRowDefinition == null) - { - matchingRowDefinition = match.BestMatch as TableRowDefinition; - } + matchingRowDefinition ??= match.BestMatch as TableRowDefinition; if (matchingRowDefinition == null) { @@ -254,10 +245,7 @@ private List GetActiveTableRowDefinition(TableControlBod } } - if (matchingRowDefinition == null) - { - matchingRowDefinition = match.BestMatch as TableRowDefinition; - } + matchingRowDefinition ??= match.BestMatch as TableRowDefinition; } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index 091903f011a..90d007adb2a 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -391,7 +391,6 @@ private byte[] ConvertToBytes(object inputObject) byte[] result = null; int elements = 1; bool isArray = false; - bool isBool = false; bool isEnum = false; if (baseType.IsArray) { @@ -424,11 +423,6 @@ private byte[] ConvertToBytes(object inputObject) _lastInputType = baseType; } - if (baseType == typeof(bool)) - { - isBool = true; - } - var elementSize = Marshal.SizeOf(baseType); result = new byte[elementSize * elements]; if (!isArray) @@ -450,11 +444,6 @@ private byte[] ConvertToBytes(object inputObject) { toBytes = Convert.ChangeType(obj, baseType); } - else if (isBool) - { - // bool is 1 byte apparently - toBytes = Convert.ToByte(obj); - } else { toBytes = obj; diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUnique.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUnique.cs index b43a870dccb..bcf7e31ccf1 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUnique.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUnique.cs @@ -72,10 +72,7 @@ protected override void ProcessRecord() else if (AsString) { string inputString = InputObject.ToString(); - if (_lastObjectAsString == null) - { - _lastObjectAsString = _lastObject.ToString(); - } + _lastObjectAsString ??= _lastObject.ToString(); if (string.Equals( inputString, @@ -91,13 +88,10 @@ protected override void ProcessRecord() } else // compare as objects { - if (_comparer == null) - { - _comparer = new ObjectCommandComparer( - true, // ascending (doesn't matter) - CultureInfo.CurrentCulture, - true); // case-sensitive - } + _comparer ??= new ObjectCommandComparer( + true, // ascending (doesn't matter) + CultureInfo.CurrentCulture, + true); // case-sensitive isUnique = (_comparer.Compare(InputObject, _lastObject) != 0); } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Group-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Group-Object.cs index 961826b30fc..a15c3f87f01 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Group-Object.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Group-Object.cs @@ -392,10 +392,7 @@ protected override void ProcessRecord() if (!_hasProcessedFirstInputObject) { - if (Property == null) - { - Property = OrderByProperty.GetDefaultKeyPropertySet(InputObject); - } + Property ??= OrderByProperty.GetDefaultKeyPropertySet(InputObject); _orderByProperty.ProcessExpressionParameter(this, Property); diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs index 325632c44ce..f0044dd1885 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs @@ -443,10 +443,7 @@ public string[] Module set { - if (value == null) - { - value = Array.Empty(); - } + value ??= Array.Empty(); _PSSnapins = value; _commandParameterSpecified = true; @@ -1044,10 +1041,7 @@ private List RehydrateList(string commandName, PSObject deserializedObject private List RehydrateList(string commandName, object deserializedList, Func itemRehydrator) { - if (itemRehydrator == null) - { - itemRehydrator = (PSObject pso) => ConvertTo(commandName, pso); - } + itemRehydrator ??= (PSObject pso) => ConvertTo(commandName, pso); List result = null; @@ -1068,17 +1062,14 @@ private List RehydrateList(string commandName, object deserializedList, Fu private Dictionary RehydrateDictionary( string commandName, - PSObject deserializedObject, + PSObject deserializedObject, string propertyName, Func valueRehydrator) { Dbg.Assert(deserializedObject != null, "deserializedObject parameter != null"); Dbg.Assert(!string.IsNullOrEmpty(propertyName), "propertyName parameter != null"); - if (valueRehydrator == null) - { - valueRehydrator = (PSObject pso) => ConvertTo(commandName, pso); - } + valueRehydrator ??= (PSObject pso) => ConvertTo(commandName, pso); Dictionary result = new(); PSPropertyInfo deserializedDictionaryProperty = deserializedObject.Properties[propertyName]; @@ -2109,7 +2100,9 @@ private void GenerateModuleHeader(TextWriter writer) // In Win8, we are no longer loading all assemblies by default. // So we need to use the fully qualified name when accessing a type in that assembly - string versionOfScriptGenerator = "[" + typeof(ExportPSSessionCommand).AssemblyQualifiedName + "]" + "::VersionOfScriptGenerator"; + Type type = typeof(ExportPSSessionCommand); + string asmName = type.Assembly.GetName().Name; + string versionOfScriptGenerator = $"[{type.FullName}, {asmName}]::VersionOfScriptGenerator"; GenerateTopComment(writer); writer.Write( HeaderTemplate, @@ -2823,13 +2816,14 @@ private void GenerateHelperFunctions(TextWriter writer) $clientSideParameters = Get-PSImplicitRemotingClientSideParameters $PSBoundParameters ${8} - $scriptCmd = {{ & $script:InvokeCommand ` - @clientSideParameters ` - -HideComputerName ` - -Session (Get-PSImplicitRemotingSession -CommandName '{0}') ` - -Arg ('{0}', $PSBoundParameters, $positionalArguments) ` - -Script {{ param($name, $boundParams, $unboundParams) & $name @boundParams @unboundParams }} ` - }} + $scriptCmd = {{ + & $script:InvokeCommand ` + @clientSideParameters ` + -HideComputerName ` + -Session (Get-PSImplicitRemotingSession -CommandName '{0}') ` + -Arg ('{0}', $PSBoundParameters, $positionalArguments) ` + -Script {{ param($name, $boundParams, $unboundParams) & $name @boundParams @unboundParams }} ` + }} $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($myInvocation.ExpectingInput, $ExecutionContext) @@ -3057,10 +3051,7 @@ internal List GenerateProxyModule( FileShare.None); using (TextWriter writer = new StreamWriter(psm1, encoding)) { - if (listOfCommandMetadata == null) - { - listOfCommandMetadata = new List(); - } + listOfCommandMetadata ??= new List(); GenerateModuleHeader(writer); GenerateHelperFunctions(writer); @@ -3078,10 +3069,7 @@ internal List GenerateProxyModule( FileShare.None); using (TextWriter writer = new StreamWriter(formatPs1xml, encoding)) { - if (listOfFormatData == null) - { - listOfFormatData = new List(); - } + listOfFormatData ??= new List(); GenerateFormatFile(writer, listOfFormatData); formatPs1xml.SetLength(formatPs1xml.Position); diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs index e6a99c38cb0..cf483b16a93 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs @@ -561,8 +561,7 @@ private void AnalyzeObjectProperties(PSObject inObj) /// The value to analyze. private void AnalyzeValue(string propertyName, object objValue) { - if (propertyName == null) - propertyName = thisObject; + propertyName ??= thisObject; Statistics stat = _statistics.EnsureEntry(propertyName); @@ -792,9 +791,9 @@ private void WritePropertyNotFoundError(string propertyName, string errorId) { Diagnostics.Assert(Property != null, "no property and no InputObject should have been addressed"); ErrorRecord errorRecord = new( - PSTraceSource.NewArgumentException("Property"), + PSTraceSource.NewArgumentException(propertyName), errorId, - ErrorCategory.InvalidArgument, + ErrorCategory.ObjectNotFound, null); errorRecord.ErrorDetails = new ErrorDetails( this, "MeasureObjectStrings", "PropertyNotFound", propertyName); @@ -820,9 +819,12 @@ protected override void EndProcessing() Statistics stat = _statistics[propertyName]; if (stat.count == 0 && Property != null) { - // Why are there two different ids for this error? - string errorId = (IsMeasuringGeneric) ? "GenericMeasurePropertyNotFound" : "TextMeasurePropertyNotFound"; - WritePropertyNotFoundError(propertyName, errorId); + if (Context.IsStrictVersion(2)) + { + string errorId = (IsMeasuringGeneric) ? "GenericMeasurePropertyNotFound" : "TextMeasurePropertyNotFound"; + WritePropertyNotFoundError(propertyName, errorId); + } + continue; } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewTimeSpanCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewTimeSpanCommand.cs index 53c39250763..a5d784da9bb 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewTimeSpanCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewTimeSpanCommand.cs @@ -88,6 +88,12 @@ public DateTime End [Parameter(ParameterSetName = "Time")] public int Seconds { get; set; } + /// + /// Allows the user to override the millisecond. + /// + [Parameter(ParameterSetName = "Time")] + public int Milliseconds { get; set; } + #endregion #region methods @@ -119,7 +125,7 @@ protected override void ProcessRecord() break; case "Time": - result = new TimeSpan(Days, Hours, Minutes, Seconds); + result = new TimeSpan(Days, Hours, Minutes, Seconds, Milliseconds); break; default: diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/OrderObjectBase.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/OrderObjectBase.cs index 40f06e58eca..519b472620b 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/OrderObjectBase.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/OrderObjectBase.cs @@ -259,10 +259,7 @@ internal void ProcessExpressionParameter( } else { - if (_unExpandedParametersWithWildCardPattern == null) - { - _unExpandedParametersWithWildCardPattern = new List(); - } + _unExpandedParametersWithWildCardPattern ??= new List(); _unExpandedParametersWithWildCardPattern.Add(unexpandedParameter); } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/PSBreakpointCommandBase.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/PSBreakpointCommandBase.cs index e3590e380d4..61d7236977d 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/PSBreakpointCommandBase.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/PSBreakpointCommandBase.cs @@ -30,10 +30,7 @@ public abstract class PSBreakpointCommandBase : PSCmdlet /// protected override void BeginProcessing() { - if (Runspace == null) - { - Runspace = Context.CurrentRunspace; - } + Runspace ??= Context.CurrentRunspace; } #endregion overrides diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/SetDateCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/SetDateCommand.cs index 8128c1a84ca..3ace3ea0e3f 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/SetDateCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/SetDateCommand.cs @@ -71,7 +71,13 @@ protected override void ProcessRecord() if (ShouldProcess(dateToUse.ToString())) { #if UNIX - if (!Platform.NonWindowsSetDate(dateToUse)) + // We are not validating the native call here. + // We just want to be sure that we're using the value the user provided us. + if (Dbg.Internal.InternalTestHooks.SetDate) + { + WriteObject(dateToUse); + } + else if (!Platform.NonWindowsSetDate(dateToUse)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } @@ -86,16 +92,23 @@ protected override void ProcessRecord() systemTime.Second = (ushort)dateToUse.Second; systemTime.Milliseconds = (ushort)dateToUse.Millisecond; #pragma warning disable 56523 - if (!NativeMethods.SetLocalTime(ref systemTime)) + if (Dbg.Internal.InternalTestHooks.SetDate) { - throw new Win32Exception(Marshal.GetLastWin32Error()); + WriteObject(systemTime); } - - // MSDN says to call this twice to account for changes - // between DST - if (!NativeMethods.SetLocalTime(ref systemTime)) + else { - throw new Win32Exception(Marshal.GetLastWin32Error()); + if (!NativeMethods.SetLocalTime(ref systemTime)) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + // MSDN says to call this twice to account for changes + // between DST + if (!NativeMethods.SetLocalTime(ref systemTime)) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } } #pragma warning restore 56523 #endif @@ -106,7 +119,11 @@ protected override void ProcessRecord() PSNoteProperty note = new("DisplayHint", DisplayHint); outputObj.Properties.Add(note); - WriteObject(outputObj); + // If we've turned on the SetDate test hook, don't emit the output object here because we emitted it earlier. + if (!Dbg.Internal.InternalTestHooks.SetDate) + { + WriteObject(outputObj); + } } #endregion diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs index 63ee15f6f59..3f40ec3439e 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs @@ -224,10 +224,7 @@ private void ProcessMarkdownInfo(MarkdownInfo markdownInfo) /// protected override void EndProcessing() { - if (_powerShell != null) - { - _powerShell.Dispose(); - } + _powerShell?.Dispose(); } } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Sort-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Sort-Object.cs index 06f5ebfffad..365a669c186 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Sort-Object.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Sort-Object.cs @@ -147,7 +147,7 @@ private int Heapify(List dataToSort, OrderByPropertyCompar // Tracking the index is necessary so that unsortable items can be output at the end, in the order // in which they were received. - for (int dataIndex = 0, discardedDuplicates = 0; dataIndex < dataToSort.Count - discardedDuplicates; dataIndex++) + for (int dataIndex = 0, discardedDuplicates = 0; dataIndex + discardedDuplicates < dataToSort.Count; dataIndex++) { // Min-heap: if the heap is full and the root item is larger than the entry, discard the entry // Max-heap: if the heap is full and the root item is smaller than the entry, discard the entry @@ -157,20 +157,19 @@ private int Heapify(List dataToSort, OrderByPropertyCompar } // If we're doing a unique sort and the entry is not unique, discard the duplicate entry - if (Unique && !uniqueSet.Add(dataToSort[dataIndex])) + if (Unique && !uniqueSet.Add(dataToSort[dataIndex + discardedDuplicates])) { discardedDuplicates++; - if (dataIndex != dataToSort.Count - discardedDuplicates) - { - // When discarding duplicates, replace them with an item at the end of the list and - // adjust our counter so that we check the item we just swapped in next - dataToSort[dataIndex] = dataToSort[dataToSort.Count - discardedDuplicates]; - dataIndex--; - } - + dataIndex--; continue; } + // Shift next non-duplicate entry into place + if (discardedDuplicates > 0) + { + dataToSort[dataIndex] = dataToSort[dataIndex + discardedDuplicates]; + } + // Add the current item to the heap and bubble it up into the correct position int childIndex = dataIndex; while (childIndex > 0) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/StartSleepCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/StartSleepCommand.cs index a57d635f3eb..36849814375 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/StartSleepCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/StartSleepCommand.cs @@ -55,6 +55,15 @@ public void Dispose() [Alias("ms")] public int Milliseconds { get; set; } + /// + /// Allows sleep time to be specified as a TimeSpan. + /// + [Parameter(Position = 0, Mandatory = true, ParameterSetName = "FromTimeSpan", ValueFromPipeline = true, + ValueFromPipelineByPropertyName = true)] + [ValidateRange(ValidateRangeKind.NonNegative)] + [Alias("ts")] + public TimeSpan Duration { get; set; } + #endregion #region methods @@ -82,10 +91,7 @@ private void Sleep(int milliSecondsToSleep) } } - if (_waitHandle != null) - { - _waitHandle.WaitOne(milliSecondsToSleep, true); - } + _waitHandle?.WaitOne(milliSecondsToSleep, true); } /// @@ -104,6 +110,26 @@ protected override void ProcessRecord() case "Milliseconds": sleepTime = Milliseconds; break; + + case "FromTimeSpan": + if (Duration.TotalMilliseconds > int.MaxValue) + { + PSArgumentException argumentException = PSTraceSource.NewArgumentException( + nameof(Duration), + StartSleepStrings.MaximumDurationExceeded, + TimeSpan.FromMilliseconds(int.MaxValue), + Duration); + + ThrowTerminatingError( + new ErrorRecord( + argumentException, + "MaximumDurationExceeded", + ErrorCategory.InvalidArgument, + targetObject: null)); + } + + sleepTime = (int)Math.Floor(Duration.TotalMilliseconds); + break; default: Dbg.Diagnostics.Assert(false, "Only one of the specified parameter sets should be called."); @@ -121,10 +147,7 @@ protected override void StopProcessing() lock (_syncObject) { _stopping = true; - if (_waitHandle != null) - { - _waitHandle.Set(); - } + _waitHandle?.Set(); } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/TestJsonCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/TestJsonCommand.cs index cfee0f78aa4..9d792b91233 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/TestJsonCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/TestJsonCommand.cs @@ -17,6 +17,7 @@ namespace Microsoft.PowerShell.Commands /// This class implements Test-Json command. /// [Cmdlet(VerbsDiagnostic.Test, "Json", DefaultParameterSetName = ParameterAttribute.AllParameterSets, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096609")] + [OutputType(typeof(bool))] public class TestJsonCommand : PSCmdlet { private const string SchemaFileParameterSet = "SchemaFile"; @@ -135,12 +136,11 @@ e is SecurityException /// protected override void ProcessRecord() { - JObject parsedJson = null; bool result = true; try { - parsedJson = JObject.Parse(Json); + var parsedJson = JToken.Parse(Json); if (_jschema != null) { diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UnblockFile.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UnblockFile.cs index 5c297ed86d5..6633a66f96f 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UnblockFile.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UnblockFile.cs @@ -137,7 +137,7 @@ protected override void ProcessRecord() } } #else - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + if (Platform.IsLinux) { string errorMessage = UnblockFileStrings.LinuxNotSupported; Exception e = new PlatformNotSupportedException(errorMessage); diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-List.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-List.cs index 5850eaca7db..b5659f8e904 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-List.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-List.cs @@ -83,10 +83,7 @@ protected override void ProcessRecord() } else { - if (_listModifier == null) - { - _listModifier = CreatePSListModifier(); - } + _listModifier ??= CreatePSListModifier(); PSMemberInfo memberInfo = InputObject.Members[Property]; if (memberInfo != null) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs index d137524629d..8e07a80384d 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs @@ -849,8 +849,7 @@ private void ProcessTypeFiles() } else if (sste.FileName != null) { - bool unused; - Context.TypeTable.Update(sste.FileName, sste.FileName, errors, Context.AuthorizationManager, Context.InitialSessionState.Host, out unused); + Context.TypeTable.Update(sste.FileName, sste.FileName, errors, Context.AuthorizationManager, Context.InitialSessionState.Host, out _); } else { @@ -1161,10 +1160,7 @@ protected override void ProcessRecord() indicesToRemove.Sort(); for (int i = indicesToRemove.Count - 1; i >= 0; i--) { - if (Context.InitialSessionState != null) - { - Context.InitialSessionState.Types.RemoveItem(indicesToRemove[i]); - } + Context.InitialSessionState?.Types.RemoveItem(indicesToRemove[i]); } try diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Var.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Var.cs index e228b309df2..6df552ab8e8 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Var.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Var.cs @@ -38,10 +38,7 @@ protected string[] IncludeFilters set { - if (value == null) - { - value = Array.Empty(); - } + value ??= Array.Empty(); _include = value; } @@ -61,10 +58,7 @@ protected string[] ExcludeFilters set { - if (value == null) - { - value = Array.Empty(); - } + value ??= Array.Empty(); _exclude = value; } @@ -258,10 +252,7 @@ public string[] Name set { - if (value == null) - { - value = new string[] { "*" }; - } + value ??= new string[] { "*" }; _name = value; } @@ -374,6 +365,7 @@ protected override void ProcessRecord() /// [Cmdlet(VerbsCommon.New, "Variable", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Low, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097121")] + [OutputType(typeof(PSVariable))] public sealed class NewVariableCommand : VariableCommandBase { #region parameters @@ -740,10 +732,7 @@ protected override void ProcessRecord() { if (Value != AutomationNull.Value) { - if (_valueList == null) - { - _valueList = new List(); - } + _valueList ??= new List(); _valueList.Add(Value); } @@ -870,10 +859,7 @@ private void SetVariable(string[] varNames, object varValue) newVarValue, newOptions); - if (Description == null) - { - Description = string.Empty; - } + Description ??= string.Empty; varToSet.Description = Description; @@ -1102,10 +1088,7 @@ protected override void ProcessRecord() // Removal of variables only happens in the local scope if the // scope wasn't explicitly specified by the user. - if (Scope == null) - { - Scope = "local"; - } + Scope ??= "local"; foreach (string varName in Name) { diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs index 969dd8231c8..7c922af0898 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs @@ -199,41 +199,23 @@ private static PSObject CreateHtmlObject(string html, string tagName) private static void EnsureHtmlParser() { - if (s_tagRegex == null) - { - s_tagRegex = new Regex(@"<\w+((\s+[^""'>/=\s\p{Cc}]+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)+\s*|\s*)/?>", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - } + s_tagRegex ??= new Regex(@"<\w+((\s+[^""'>/=\s\p{Cc}]+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)+\s*|\s*)/?>", + RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - if (s_attribsRegex == null) - { - s_attribsRegex = new Regex(@"(?<=\s+)([^""'>/=\s\p{Cc}]+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - } + s_attribsRegex ??= new Regex(@"(?<=\s+)([^""'>/=\s\p{Cc}]+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)", + RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - if (s_attribNameValueRegex == null) - { - s_attribNameValueRegex = new Regex(@"([^""'>/=\s\p{Cc}]+)(?:\s*=\s*(?:""(.*?)""|'(.*?)'|([^'"">\s]+)))?", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - } + s_attribNameValueRegex ??= new Regex(@"([^""'>/=\s\p{Cc}]+)(?:\s*=\s*(?:""(.*?)""|'(.*?)'|([^'"">\s]+)))?", + RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - if (s_inputFieldRegex == null) - { - s_inputFieldRegex = new Regex(@"]*(/>|>.*?)", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - } + s_inputFieldRegex ??= new Regex(@"]*(/?>|>.*?)", + RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - if (s_linkRegex == null) - { - s_linkRegex = new Regex(@"]*(/>|>.*?)", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - } + s_linkRegex ??= new Regex(@"]*(/>|>.*?)", + RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - if (s_imageRegex == null) - { - s_imageRegex = new Regex(@"]*?>", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - } + s_imageRegex ??= new Regex(@"]*?>", + RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); } private void InitializeRawContent(HttpResponseMessage baseResponse) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/HttpVersionCompletionsAttribute.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/HttpVersionCompletionsAttribute.cs new file mode 100644 index 00000000000..05eb8ed96b6 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/HttpVersionCompletionsAttribute.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Management.Automation; +using System.Net; +using System.Reflection; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// A completer for HTTP version names. + /// + internal sealed class HttpVersionCompletionsAttribute : ArgumentCompletionsAttribute + { + public static readonly string[] AllowedVersions; + + static HttpVersionCompletionsAttribute() + { + FieldInfo[] fields = typeof(HttpVersion).GetFields(BindingFlags.Static | BindingFlags.Public); + + var versions = new List(fields.Length - 1); + + for (int i = 0; i < fields.Length; i++) + { + // skip field Unknown and not Version type + if (fields[i].Name == nameof(HttpVersion.Unknown) || fields[i].FieldType != typeof(Version)) + { + continue; + } + + var version = (Version?)fields[i].GetValue(null); + + if (version is not null) + { + versions.Add(version.ToString()); + } + } + + AllowedVersions = versions.ToArray(); + } + + /// + public HttpVersionCompletionsAttribute() : base(AllowedVersions) + { + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs index 85860aa19e6..692c895d211 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs @@ -63,14 +63,14 @@ public enum WebSslProtocol Default = 0, /// - /// Specifies the TLS 1.0 security protocol. The TLS protocol is defined in IETF RFC 2246. + /// Specifies the TLS 1.0 is obsolete. Using this value now defaults to TLS 1.2. /// - Tls = SslProtocols.Tls, + Tls = SslProtocols.Tls12, /// - /// Specifies the TLS 1.1 security protocol. The TLS protocol is defined in IETF RFC 4346. + /// Specifies the TLS 1.1 is obsolete. Using this value now defaults to TLS 1.2. /// - Tls11 = SslProtocols.Tls11, + Tls11 = SslProtocols.Tls12, /// /// Specifies the TLS 1.2 security protocol. The TLS protocol is defined in IETF RFC 5246. @@ -107,6 +107,18 @@ public abstract partial class WebRequestPSCmdlet : PSCmdlet #endregion + #region HTTP Version + + /// + /// Gets or sets the HTTP Version property. + /// + [Parameter] + [ArgumentToVersionTransformation] + [HttpVersionCompletions] + public virtual Version HttpVersion { get; set; } + + #endregion + #region Session /// /// Gets or sets the Session property. @@ -571,10 +583,7 @@ internal virtual void ValidateParameters() internal virtual void PrepareSession() { // make sure we have a valid WebRequestSession object to work with - if (WebSession == null) - { - WebSession = new WebRequestSession(); - } + WebSession ??= new WebRequestSession(); if (SessionVariable != null) { @@ -1081,6 +1090,11 @@ internal virtual HttpRequestMessage GetRequest(Uri uri) // create the base WebRequest object var request = new HttpRequestMessage(httpMethod, requestUri); + if (HttpVersion is not null) + { + request.Version = HttpVersion; + } + // pull in session data if (WebSession.Headers.Count > 0) { @@ -1260,9 +1274,15 @@ internal virtual void FillRequestStream(HttpRequestMessage request) } } - // Add the content headers - if (request.Content == null) + // For other methods like Put where empty content has meaning, we need to fill in the content + if (request.Content is null) { + // If this is a Get request and there is no content, then don't fill in the content as empty content gets rejected by some web services per RFC7230 + if ((IsStandardMethodSet() && request.Method == HttpMethod.Get && ContentType is null) || (IsCustomMethodSet() && CustomMethod.ToUpperInvariant() == "GET")) + { + return; + } + request.Content = new StringContent(string.Empty); request.Content.Headers.Clear(); } @@ -1413,8 +1433,8 @@ internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestM string reqVerboseMsg = string.Format( CultureInfo.CurrentCulture, WebCmdletStrings.WebMethodInvocationVerboseMsg, + requestWithoutRange.Version, requestWithoutRange.Method, - requestWithoutRange.RequestUri, requestContentLength); WriteVerbose(reqVerboseMsg); @@ -1505,10 +1525,13 @@ protected override void ProcessRecord() if (request.Content != null) requestContentLength = request.Content.Headers.ContentLength.Value; - string reqVerboseMsg = string.Format(CultureInfo.CurrentCulture, + string reqVerboseMsg = string.Format( + CultureInfo.CurrentCulture, WebCmdletStrings.WebMethodInvocationVerboseMsg, + request.Version, request.Method, requestContentLength); + WriteVerbose(reqVerboseMsg); HttpResponseMessage response = GetResponse(client, request, keepAuthorization); @@ -1555,10 +1578,7 @@ protected override void ProcessRecord() } finally { - if (reader != null) - { - reader.Dispose(); - } + reader?.Dispose(); } if (!string.IsNullOrEmpty(detailMsg)) @@ -1634,13 +1654,7 @@ protected override void ProcessRecord() /// /// Implementing ^C, after start the BeginGetResponse. /// - protected override void StopProcessing() - { - if (_cancelToken != null) - { - _cancelToken.Cancel(); - } - } + protected override void StopProcessing() => _cancelToken?.Cancel(); #endregion Overrides @@ -1830,7 +1844,7 @@ internal void ParseLinkHeader(HttpResponseMessage response, System.Uri requestUr // we only support the URL in angle brackets and `rel`, other attributes are ignored // user can still parse it themselves via the Headers property - const string pattern = "<(?.*?)>;\\s*rel=(\"?)(?.*?)\\1[^\\w -.]?"; + const string pattern = "<(?.*?)>;\\s*rel=(?\")?(?(?(quoted).*?|[^,;]*))(?(quoted)\")"; IEnumerable links; if (response.Headers.TryGetValues("Link", out links)) { diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs index 98e7c397e45..1d80bcfdb2b 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs @@ -123,10 +123,7 @@ public Dictionary> Headers { get { - if (_headers == null) - { - _headers = WebResponseHelper.GetHeadersDictionary(BaseResponse); - } + _headers ??= WebResponseHelper.GetHeadersDictionary(BaseResponse); return _headers; } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertFromJsonCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertFromJsonCommand.cs index 0af97b061a9..392a58e13d4 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertFromJsonCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertFromJsonCommand.cs @@ -86,7 +86,7 @@ protected override void EndProcessing() catch (ArgumentException) { // The first input string does not represent a complete Json Syntax. - // Hence consider the the entire input as a single Json content. + // Hence consider the entire input as a single Json content. } if (successfullyConverted) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertToJsonCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertToJsonCommand.cs index 36e8acdfd6e..eb8c3e3cc08 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertToJsonCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertToJsonCommand.cs @@ -16,6 +16,7 @@ namespace Microsoft.PowerShell.Commands /// This command converts an object to a Json string representation. /// [Cmdlet(VerbsData.ConvertTo, "Json", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096925", RemotingCapability = RemotingCapability.None)] + [OutputType(typeof(string))] public class ConvertToJsonCommand : PSCmdlet, IDisposable { /// @@ -27,15 +28,13 @@ public class ConvertToJsonCommand : PSCmdlet, IDisposable private int _depth = 2; - private const int maxDepthAllowed = 100; - private readonly CancellationTokenSource _cancellationSource = new(); /// /// Gets or sets the Depth property. /// [Parameter] - [ValidateRange(0, int.MaxValue)] + [ValidateRange(0, 100)] public int Depth { get { return _depth; } @@ -99,23 +98,7 @@ protected virtual void Dispose(bool disposing) _cancellationSource.Dispose(); } } - - /// - /// Prerequisite checks. - /// - protected override void BeginProcessing() - { - if (_depth > maxDepthAllowed) - { - string errorMessage = StringUtil.Format(WebCmdletStrings.ReachedMaximumDepthAllowed, maxDepthAllowed); - ThrowTerminatingError(new ErrorRecord( - new InvalidOperationException(errorMessage), - "ReachedMaximumDepthAllowed", - ErrorCategory.InvalidOperation, - null)); - } - } - + private readonly List _inputObjects = new(); /// diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs index dafcf3b4295..1a46d074048 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs @@ -13,6 +13,7 @@ namespace Microsoft.PowerShell.Commands /// This command makes an HTTP or HTTPS request to a web server and returns the results. /// [Cmdlet(VerbsLifecycle.Invoke, "WebRequest", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097126", DefaultParameterSetName = "StandardMethod")] + [OutputType(typeof(BasicHtmlWebResponseObject))] public class InvokeWebRequestCommand : WebRequestPSCmdlet { #region Virtual Method Overrides diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObject.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObject.cs index ff576c7e68f..8e156152020 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObject.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObject.cs @@ -160,22 +160,6 @@ public static object ConvertFromJson(string input, bool returnHashtable, int? ma error = null; try { - // JsonConvert.DeserializeObject does not throw an exception when an invalid Json array is passed. - // This issue is being tracked by https://github.com/JamesNK/Newtonsoft.Json/issues/1930. - // To work around this, we need to identify when input is a Json array, and then try to parse it via JArray.Parse(). - - // If input starts with '[' (ignoring white spaces). - if (Regex.Match(input, @"^\s*\[").Success) - { - // JArray.Parse() will throw a JsonException if the array is invalid. - // This will be caught by the catch block below, and then throw an - // ArgumentException - this is done to have same behavior as the JavaScriptSerializer. - JArray.Parse(input); - - // Please note that if the Json array is valid, we don't do anything, - // we just continue the deserialization. - } - var obj = JsonConvert.DeserializeObject( input, new JsonSerializerSettings @@ -344,7 +328,7 @@ private static ICollection PopulateFromJArray(JArray list, out ErrorReco private static Hashtable PopulateHashTableFromJDictionary(JObject entries, out ErrorRecord error) { error = null; - Hashtable result = new(entries.Count); + OrderedHashtable result = new(entries.Count); foreach (var entry in entries) { // Case sensitive duplicates should normally not occur since JsonConvert.DeserializeObject diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/StreamHelper.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/StreamHelper.cs index 43b625311b9..43b42066cfa 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/StreamHelper.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/StreamHelper.cs @@ -454,11 +454,8 @@ internal static string DecodeStream(Stream stream, ref Encoding encoding) internal static byte[] EncodeToBytes(string str, Encoding encoding) { - if (encoding == null) - { - // just use the default encoding if one wasn't provided - encoding = ContentHelper.GetDefaultEncoding(); - } + // just use the default encoding if one wasn't provided + encoding ??= ContentHelper.GetDefaultEncoding(); return encoding.GetBytes(str); } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestSession.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestSession.cs index ae08babe17f..c100f345a79 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestSession.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestSession.cs @@ -104,10 +104,7 @@ public WebRequestSession() /// The certificate to be added. internal void AddCertificate(X509Certificate certificate) { - if (Certificates == null) - { - Certificates = new X509CertificateCollection(); - } + Certificates ??= new X509CertificateCollection(); Certificates.Add(certificate); } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write.cs index 40c4ec63568..55e98260779 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write.cs @@ -312,10 +312,7 @@ protected override void ProcessRecord() { Exception e = this.Exception; string msg = Message; - if (e == null) - { - e = new WriteErrorException(msg); - } + e ??= new WriteErrorException(msg); string errid = ErrorId; if (string.IsNullOrEmpty(errid)) @@ -339,10 +336,7 @@ protected override void ProcessRecord() string recact = RecommendedAction; if (!string.IsNullOrEmpty(recact)) { - if (errorRecord.ErrorDetails == null) - { - errorRecord.ErrorDetails = new ErrorDetails(errorRecord.ToString()); - } + errorRecord.ErrorDetails ??= new ErrorDetails(errorRecord.ToString()); errorRecord.ErrorDetails.RecommendedAction = recact; } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs index 8db56196a68..74a8ee1b7c9 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs @@ -453,8 +453,7 @@ protected override void ProcessRecord() { CreateMemoryStream(); - if (_serializer != null) - _serializer.SerializeAsStream(InputObject); + _serializer?.SerializeAsStream(InputObject); if (_serializer != null) { @@ -472,8 +471,7 @@ protected override void ProcessRecord() } else { - if (_serializer != null) - _serializer.Serialize(InputObject); + _serializer?.Serialize(InputObject); } } @@ -801,13 +799,7 @@ internal void Import() } } - internal void Stop() - { - if (_deserializer != null) - { - _deserializer.Stop(); - } - } + internal void Stop() => _deserializer?.Stop(); } #region Select-Xml diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceExpressionCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceExpressionCommand.cs index 412f4a2d150..7ae437525dd 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceExpressionCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceExpressionCommand.cs @@ -249,13 +249,7 @@ protected override void EndProcessing() /// /// Ensures that the sub-pipeline we created gets stopped as well. /// - protected override void StopProcessing() - { - if (_pipeline != null) - { - _pipeline.Stop(); - } - } + protected override void StopProcessing() => _pipeline?.Stop(); #endregion Cmdlet code diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/StartSleepStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/StartSleepStrings.resx new file mode 100644 index 00000000000..32804b9e21b --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/StartSleepStrings.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The '-Duration' parameter value must not exceed '{0}', provided value was '{1}'. + + diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/WebCmdletStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/WebCmdletStrings.resx index e3c64bd5ca3..976ec531e25 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/WebCmdletStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/WebCmdletStrings.resx @@ -129,7 +129,7 @@ The cmdlet cannot run because the following parameter is not specified: Credential. The supplied Authentication type requires a Credential. Specify Credential, then retry. - + The cmdlet cannot run because the following parameter is not specified: Token. The supplied Authentication type requires a Token. Specify Token, then retry. @@ -237,9 +237,6 @@ Ensure 'Json.Net.psd1' and 'Newtonsoft.Json.dll' are available in a versioned subdirectory of '{0}'. - - The maximum depth allowed for serialization is {0}. - Conversion from JSON failed with error: {0} @@ -250,7 +247,7 @@ Following rel link {0} - {0} with {1}-byte payload + HTTP/{0} {1} with {2}-byte payload The remote server indicated it could not resume downloading. The local file will be overwritten. diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs index 6908320db53..0b54bcff24a 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs @@ -1,6 +1,5 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. - #nullable enable using System; @@ -187,6 +186,7 @@ internal static int MaxNameLength() "nologo", "noninteractive", "noprofile", + "noprofileloadtime", "outputformat", "removeworkingdirectorytrailingcharacter", "settingsfile", @@ -195,6 +195,59 @@ internal static int MaxNameLength() "workingdirectory" }; + /// + /// These represent the parameters that are used when starting pwsh. + /// We can query in our telemetry to determine how pwsh was invoked. + /// + [Flags] + internal enum ParameterBitmap : long + { + Command = 0x00000001, // -Command | -c + ConfigurationName = 0x00000002, // -ConfigurationName | -config + CustomPipeName = 0x00000004, // -CustomPipeName + EncodedCommand = 0x00000008, // -EncodedCommand | -e | -ec + EncodedArgument = 0x00000010, // -EncodedArgument + ExecutionPolicy = 0x00000020, // -ExecutionPolicy | -ex | -ep + File = 0x00000040, // -File | -f + Help = 0x00000080, // -Help, -?, /? + InputFormat = 0x00000100, // -InputFormat | -inp | -if + Interactive = 0x00000200, // -Interactive | -i + Login = 0x00000400, // -Login | -l + MTA = 0x00000800, // -MTA + NoExit = 0x00001000, // -NoExit | -noe + NoLogo = 0x00002000, // -NoLogo | -nol + NonInteractive = 0x00004000, // -NonInteractive | -noni + NoProfile = 0x00008000, // -NoProfile | -nop + OutputFormat = 0x00010000, // -OutputFormat | -o | -of + SettingsFile = 0x00020000, // -SettingsFile | -settings + SSHServerMode = 0x00040000, // -SSHServerMode | -sshs + SocketServerMode = 0x00080000, // -SocketServerMode | -sockets + ServerMode = 0x00100000, // -ServerMode | -server + NamedPipeServerMode = 0x00200000, // -NamedPipeServerMode | -namedpipes + STA = 0x00400000, // -STA + Version = 0x00800000, // -Version | -v + WindowStyle = 0x01000000, // -WindowStyle | -w + WorkingDirectory = 0x02000000, // -WorkingDirectory | -wd + ConfigurationFile = 0x04000000, // -ConfigurationFile + NoProfileLoadTime = 0x08000000, // -NoProfileLoadTime + // Enum values for specified ExecutionPolicy + EPUnrestricted = 0x0000000100000000, // ExecutionPolicy unrestricted + EPRemoteSigned = 0x0000000200000000, // ExecutionPolicy remote signed + EPAllSigned = 0x0000000400000000, // ExecutionPolicy all signed + EPRestricted = 0x0000000800000000, // ExecutionPolicy restricted + EPDefault = 0x0000001000000000, // ExecutionPolicy default + EPBypass = 0x0000002000000000, // ExecutionPolicy bypass + EPUndefined = 0x0000004000000000, // ExecutionPolicy undefined + EPIncorrect = 0x0000008000000000, // ExecutionPolicy incorrect + } + + internal ParameterBitmap ParametersUsed = 0; + + internal double ParametersUsedAsDouble + { + get { return (double)ParametersUsed; } + } + [Conditional("DEBUG")] private void AssertArgumentsParsed() { @@ -320,6 +373,15 @@ internal Collection Args } } + internal string? ConfigurationFile + { + get + { + AssertArgumentsParsed(); + return _configurationFile; + } + } + internal string? ConfigurationName { get @@ -403,6 +465,15 @@ internal bool ShowExtendedHelp } } + internal bool NoProfileLoadTime + { + get + { + AssertArgumentsParsed(); + return _noProfileLoadTime; + } + } + internal bool ShowVersion { get @@ -641,6 +712,53 @@ internal static string NormalizeFilePath(string path) return Path.GetFullPath(path); } + /// + /// Determine the execution policy based on the supplied string. + /// If the string doesn't match to any known execution policy, set it to incorrect. + /// + /// The value provided on the command line. + /// The execution policy. + private static ParameterBitmap GetExecutionPolicy(string? _executionPolicy) + { + if (_executionPolicy is null) + { + return ParameterBitmap.EPUndefined; + } + + ParameterBitmap executionPolicySetting = ParameterBitmap.EPIncorrect; + + if (string.Equals(_executionPolicy, "default", StringComparison.OrdinalIgnoreCase)) + { + executionPolicySetting = ParameterBitmap.EPDefault; + } + else if (string.Equals(_executionPolicy, "remotesigned", StringComparison.OrdinalIgnoreCase)) + { + executionPolicySetting = ParameterBitmap.EPRemoteSigned; + } + else if (string.Equals(_executionPolicy, "bypass", StringComparison.OrdinalIgnoreCase)) + { + executionPolicySetting = ParameterBitmap.EPBypass; + } + else if (string.Equals(_executionPolicy, "allsigned", StringComparison.OrdinalIgnoreCase)) + { + executionPolicySetting = ParameterBitmap.EPAllSigned; + } + else if (string.Equals(_executionPolicy, "restricted", StringComparison.OrdinalIgnoreCase)) + { + executionPolicySetting = ParameterBitmap.EPRestricted; + } + else if (string.Equals(_executionPolicy, "unrestricted", StringComparison.OrdinalIgnoreCase)) + { + executionPolicySetting = ParameterBitmap.EPUnrestricted; + } + else if (string.Equals(_executionPolicy, "undefined", StringComparison.OrdinalIgnoreCase)) + { + executionPolicySetting = ParameterBitmap.EPUndefined; + } + + return executionPolicySetting; + } + private static bool MatchSwitch(string switchKey, string match, string smallestUnambiguousMatch) { Dbg.Assert(!string.IsNullOrEmpty(match), "need a value"); @@ -690,7 +808,6 @@ private void DisplayBanner(PSHostUserInterface hostUI, string? bannerText) if (!string.IsNullOrEmpty(bannerText)) { hostUI.WriteLine(bannerText); - hostUI.WriteLine(); } if (UpdatesNotification.CanNotifyUpdates) @@ -756,6 +873,7 @@ private void ParseHelper(string[] args) _noInteractive = true; _skipUserInit = true; _noExit = false; + ParametersUsed |= ParameterBitmap.Version; break; } @@ -764,48 +882,77 @@ private void ParseHelper(string[] args) _showHelp = true; _showExtendedHelp = true; _abortStartup = true; + ParametersUsed |= ParameterBitmap.Help; } else if (MatchSwitch(switchKey, "login", "l")) { // On Windows, '-Login' does nothing. // On *nix, '-Login' is already handled much earlier to improve startup performance, so we do nothing here. + ParametersUsed |= ParameterBitmap.Login; } else if (MatchSwitch(switchKey, "noexit", "noe")) { _noExit = true; noexitSeen = true; + ParametersUsed |= ParameterBitmap.NoExit; } else if (MatchSwitch(switchKey, "noprofile", "nop")) { _skipUserInit = true; + ParametersUsed |= ParameterBitmap.NoProfile; } else if (MatchSwitch(switchKey, "nologo", "nol")) { _showBanner = false; + ParametersUsed |= ParameterBitmap.NoLogo; } else if (MatchSwitch(switchKey, "noninteractive", "noni")) { _noInteractive = true; + ParametersUsed |= ParameterBitmap.NonInteractive; } else if (MatchSwitch(switchKey, "socketservermode", "so")) { _socketServerMode = true; + ParametersUsed |= ParameterBitmap.SocketServerMode; } else if (MatchSwitch(switchKey, "servermode", "s")) { _serverMode = true; + ParametersUsed |= ParameterBitmap.ServerMode; } else if (MatchSwitch(switchKey, "namedpipeservermode", "nam")) { _namedPipeServerMode = true; + ParametersUsed |= ParameterBitmap.NamedPipeServerMode; } else if (MatchSwitch(switchKey, "sshservermode", "sshs")) { _sshServerMode = true; + ParametersUsed |= ParameterBitmap.SSHServerMode; + } + else if (MatchSwitch(switchKey, "noprofileloadtime", "noprofileloadtime")) + { + _noProfileLoadTime = true; + ParametersUsed |= ParameterBitmap.NoProfileLoadTime; } else if (MatchSwitch(switchKey, "interactive", "i")) { _noInteractive = false; + ParametersUsed |= ParameterBitmap.Interactive; + } + else if (MatchSwitch(switchKey, "configurationfile", "configurationfile")) + { + ++i; + if (i >= args.Length) + { + SetCommandLineError( + CommandLineParameterParserStrings.MissingConfigurationFileArgument); + break; + } + + _configurationFile = args[i]; + ParametersUsed |= ParameterBitmap.ConfigurationFile; } else if (MatchSwitch(switchKey, "configurationname", "config")) { @@ -818,6 +965,7 @@ private void ParseHelper(string[] args) } _configurationName = args[i]; + ParametersUsed |= ParameterBitmap.ConfigurationName; } else if (MatchSwitch(switchKey, "custompipename", "cus")) { @@ -842,7 +990,9 @@ private void ParseHelper(string[] args) break; } #endif + _customPipeName = args[i]; + ParametersUsed |= ParameterBitmap.CustomPipeName; } else if (MatchSwitch(switchKey, "command", "c")) { @@ -850,6 +1000,8 @@ private void ParseHelper(string[] args) { break; } + + ParametersUsed |= ParameterBitmap.Command; } else if (MatchSwitch(switchKey, "windowstyle", "w")) { @@ -876,6 +1028,8 @@ private void ParseHelper(string[] args) string.Format(CultureInfo.CurrentCulture, CommandLineParameterParserStrings.InvalidWindowStyleArgument, args[i], e.Message)); break; } + + ParametersUsed |= ParameterBitmap.WindowStyle; #endif } else if (MatchSwitch(switchKey, "file", "f")) @@ -884,6 +1038,8 @@ private void ParseHelper(string[] args) { break; } + + ParametersUsed |= ParameterBitmap.File; } #if DEBUG else if (MatchSwitch(switchKey, "isswait", "isswait")) @@ -895,14 +1051,18 @@ private void ParseHelper(string[] args) { ParseFormat(args, ref i, ref _outFormat, CommandLineParameterParserStrings.MissingOutputFormatParameter); _outputFormatSpecified = true; + ParametersUsed |= ParameterBitmap.OutputFormat; } else if (MatchSwitch(switchKey, "inputformat", "inp") || MatchSwitch(switchKey, "if", "if")) { ParseFormat(args, ref i, ref _inFormat, CommandLineParameterParserStrings.MissingInputFormatParameter); + ParametersUsed |= ParameterBitmap.InputFormat; } else if (MatchSwitch(switchKey, "executionpolicy", "ex") || MatchSwitch(switchKey, "ep", "ep")) { ParseExecutionPolicy(args, ref i, ref _executionPolicy, CommandLineParameterParserStrings.MissingExecutionPolicyParameter); + ParametersUsed |= ParameterBitmap.ExecutionPolicy; + ParametersUsed |= GetExecutionPolicy(_executionPolicy); } else if (MatchSwitch(switchKey, "encodedcommand", "e") || MatchSwitch(switchKey, "ec", "e")) { @@ -911,6 +1071,8 @@ private void ParseHelper(string[] args) { break; } + + ParametersUsed |= ParameterBitmap.EncodedCommand; } else if (MatchSwitch(switchKey, "encodedarguments", "encodeda") || MatchSwitch(switchKey, "ea", "ea")) { @@ -918,6 +1080,8 @@ private void ParseHelper(string[] args) { break; } + + ParametersUsed |= ParameterBitmap.EncodedArgument; } else if (MatchSwitch(switchKey, "settingsfile", "settings")) { @@ -926,6 +1090,8 @@ private void ParseHelper(string[] args) { break; } + + ParametersUsed |= ParameterBitmap.SettingsFile; } else if (MatchSwitch(switchKey, "sta", "sta")) { @@ -945,6 +1111,7 @@ private void ParseHelper(string[] args) } _staMode = true; + ParametersUsed |= ParameterBitmap.STA; } else if (MatchSwitch(switchKey, "mta", "mta")) { @@ -964,6 +1131,7 @@ private void ParseHelper(string[] args) } _staMode = false; + ParametersUsed |= ParameterBitmap.MTA; } else if (MatchSwitch(switchKey, "workingdirectory", "wo") || MatchSwitch(switchKey, "wd", "wd")) { @@ -976,6 +1144,7 @@ private void ParseHelper(string[] args) } _workingDirectory = args[i]; + ParametersUsed |= ParameterBitmap.WorkingDirectory; } #if !UNIX else if (MatchSwitch(switchKey, "removeworkingdirectorytrailingcharacter", "removeworkingdirectorytrailingcharacter")) @@ -991,6 +1160,9 @@ private void ParseHelper(string[] args) { break; } + + // default to filename being the next argument. + ParametersUsed |= ParameterBitmap.File; } } @@ -1340,7 +1512,9 @@ private bool CollectArgs(string[] args, ref int i) private bool _serverMode; private bool _namedPipeServerMode; private bool _sshServerMode; + private bool _noProfileLoadTime; private bool _showVersion; + private string? _configurationFile; private string? _configurationName; private string? _error; private bool _showHelp; diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs index 88964ea7a25..d5b81cf4dd9 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs @@ -1732,10 +1732,7 @@ internal static void ReadConsoleOutput if (origin.X + (contentsRegion.Right - contentsRegion.Left) + 1 < bufferInfo.BufferSize.X && ShouldCheck(contentsRegion.Right, contents, contentsRegion)) { - if (cellArray == null) - { - cellArray = new BufferCell[cellArrayRegion.Bottom + 1, 2]; - } + cellArray ??= new BufferCell[cellArrayRegion.Bottom + 1, 2]; checkOrigin = new Coordinates(origin.X + (contentsRegion.Right - contentsRegion.Left), origin.Y); @@ -2270,14 +2267,13 @@ Coordinates origin c.X = (short)origin.X; c.Y = (short)origin.Y; - DWORD unused = 0; bool result = NativeMethods.FillConsoleOutputCharacter( consoleHandle.DangerousGetHandle(), character, (DWORD)numberToWrite, c, - out unused); + out _); if (!result) { int err = Marshal.GetLastWin32Error(); @@ -2324,14 +2320,13 @@ Coordinates origin c.X = (short)origin.X; c.Y = (short)origin.Y; - DWORD unused = 0; bool result = NativeMethods.FillConsoleOutputAttribute( consoleHandle.DangerousGetHandle(), attribute, (DWORD)numberToWrite, c, - out unused); + out _); if (!result) { @@ -2494,6 +2489,8 @@ internal static string GetConsoleWindowTitle() return consoleTitle.ToString(); } + private static bool s_dontsetConsoleWindowTitle; + /// /// Wraps Win32 SetConsoleTitle. /// @@ -2505,12 +2502,27 @@ internal static string GetConsoleWindowTitle() /// internal static void SetConsoleWindowTitle(string consoleTitle) { + if (s_dontsetConsoleWindowTitle) + { + return; + } + bool result = NativeMethods.SetConsoleTitle(consoleTitle); if (!result) { int err = Marshal.GetLastWin32Error(); + // ERROR_GEN_FAILURE is returned if this api can't be used with the terminal + if (err == 0x1f) + { + tracer.WriteLine("Call to SetConsoleTitle failed: {0}", err); + s_dontsetConsoleWindowTitle = true; + + // We ignore this specific error as the console can still continue to operate + return; + } + HostException e = CreateHostException(err, "SetConsoleWindowTitle", ErrorCategory.ResourceUnavailable, ConsoleControlStrings.SetConsoleWindowTitleExceptionTemplate); throw e; diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index b0ae4735026..0d647a2ded1 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -16,6 +16,7 @@ using System.Management.Automation.Internal; using System.Management.Automation.Language; using System.Management.Automation.Remoting; +using System.Management.Automation.Remoting.Server; using System.Management.Automation.Runspaces; using System.Management.Automation.Tracing; using System.Reflection; @@ -72,6 +73,9 @@ internal sealed partial class ConsoleHost /// /// Help text for minishell. This is displayed on 'minishell -?'. /// + /// + /// True when an external caller provides an InitialSessionState object, which can conflict with '-ConfigurationFile' argument. + /// /// /// The exit code for the shell. /// @@ -98,7 +102,10 @@ internal sealed partial class ConsoleHost /// Anyone checking the exit code of the shell or monitor can mask off the high word to determine the exit code passed /// by the script that the shell last executed. /// - internal static int Start(string bannerText, string helpText) + internal static int Start( + string bannerText, + string helpText, + bool issProvidedExternally) { #if DEBUG if (Environment.GetEnvironmentVariable("POWERSHELL_DEBUG_STARTUP") != null) @@ -110,6 +117,12 @@ internal static int Start(string bannerText, string helpText) } #endif + // Check for external InitialSessionState configuration conflict with '-ConfigurationFile' argument. + if (issProvidedExternally && !string.IsNullOrEmpty(s_cpp.ConfigurationFile)) + { + throw new ConsoleHostStartupException(ConsoleHostStrings.ShellCannotBeStartedWithConfigConflict); + } + // put PSHOME in front of PATH so that calling `powershell` within `powershell` always starts the same running version string path = Environment.GetEnvironmentVariable("PATH"); string pshome = Utils.DefaultPowerShellAppBase + Path.PathSeparator; @@ -176,10 +189,7 @@ internal static int Start(string bannerText, string helpText) if ((s_cpp.ServerMode && s_cpp.NamedPipeServerMode) || (s_cpp.ServerMode && s_cpp.SocketServerMode) || (s_cpp.NamedPipeServerMode && s_cpp.SocketServerMode)) { s_tracer.TraceError("Conflicting server mode parameters, parameters must be used exclusively."); - if (s_theConsoleHost != null) - { - s_theConsoleHost.ui.WriteErrorLine(ConsoleHostStrings.ConflictingServerModeParameters); - } + s_theConsoleHost?.ui.WriteErrorLine(ConsoleHostStrings.ConflictingServerModeParameters); return ExitCodeBadCommandLineParameter; } @@ -187,41 +197,44 @@ internal static int Start(string bannerText, string helpText) #if !UNIX TaskbarJumpList.CreateRunAsAdministratorJumpList(); #endif - // First check for and handle PowerShell running in a server mode. if (s_cpp.ServerMode) { - ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("ServerMode"); + ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("ServerMode", s_cpp.ParametersUsedAsDouble); ProfileOptimization.StartProfile("StartupProfileData-ServerMode"); - System.Management.Automation.Remoting.Server.StdIOProcessMediator.Run( + StdIOProcessMediator.Run( initialCommand: s_cpp.InitialCommand, workingDirectory: s_cpp.WorkingDirectory, - configurationName: null); + configurationName: null, + configurationFile: s_cpp.ConfigurationFile, + combineErrOutStream: false); exitCode = 0; } else if (s_cpp.SSHServerMode) { - ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("SSHServer"); + ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("SSHServer", s_cpp.ParametersUsedAsDouble); ProfileOptimization.StartProfile("StartupProfileData-SSHServerMode"); - System.Management.Automation.Remoting.Server.StdIOProcessMediator.Run( + StdIOProcessMediator.Run( initialCommand: s_cpp.InitialCommand, workingDirectory: null, - configurationName: null); + configurationName: null, + configurationFile: s_cpp.ConfigurationFile, + combineErrOutStream: true); exitCode = 0; } else if (s_cpp.NamedPipeServerMode) { - ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("NamedPipe"); + ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("NamedPipe", s_cpp.ParametersUsedAsDouble); ProfileOptimization.StartProfile("StartupProfileData-NamedPipeServerMode"); - System.Management.Automation.Remoting.RemoteSessionNamedPipeServer.RunServerMode( + RemoteSessionNamedPipeServer.RunServerMode( configurationName: s_cpp.ConfigurationName); exitCode = 0; } else if (s_cpp.SocketServerMode) { - ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("SocketServerMode"); + ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("SocketServerMode", s_cpp.ParametersUsedAsDouble); ProfileOptimization.StartProfile("StartupProfileData-SocketServerMode"); - System.Management.Automation.Remoting.Server.HyperVSocketMediator.Run( + HyperVSocketMediator.Run( initialCommand: s_cpp.InitialCommand, configurationName: s_cpp.ConfigurationName); exitCode = 0; @@ -254,13 +267,14 @@ internal static int Start(string bannerText, string helpText) PSHost.IsStdOutputRedirected = Console.IsOutputRedirected; // Send startup telemetry for ConsoleHost startup - ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("Normal"); + ApplicationInsightsTelemetry.SendPSCoreStartupTelemetry("Normal", s_cpp.ParametersUsedAsDouble); exitCode = s_theConsoleHost.Run(s_cpp, false); } } finally { +#pragma warning disable IDE0031 if (s_theConsoleHost != null) { #if LEGACYTELEMETRY @@ -276,6 +290,7 @@ internal static int Start(string bannerText, string helpText) #endif s_theConsoleHost.Dispose(); } +#pragma warning restore IDE0031 } unchecked @@ -300,8 +315,8 @@ internal static void ParseCommandLine(string[] args) PowerShellConfig.Instance.SetSystemConfigFilePath(s_cpp.SettingsFile); } - // Check registry setting for a Group Policy ConfigurationName entry and - // use it to override anything set by the user. + // Check registry setting for a Group Policy ConfigurationName entry, + // and use it to override anything set by the user on the command line. // It depends on setting file so 'SetSystemConfigFilePath()' should be called before. s_cpp.ConfigurationName = CommandLineParameterParser.GetConfigurationNameFromGroupPolicy(); } @@ -432,7 +447,7 @@ private static void SpinUpBreakHandlerThread(bool shouldEndSession) host.ShouldEndSession = shouldEndSession; } - // Creation of the tread and starting it should be an atomic operation. + // Creation of the thread and starting it should be an atomic operation. // otherwise the code in Run method can get instance of the breakhandlerThread // after it is created and before started and call join on it. This will result // in ThreadStateException. @@ -483,10 +498,7 @@ private static void HandleBreak() if (runspaceRef != null) { var runspace = runspaceRef.Runspace; - if (runspace != null) - { - runspace.Close(); - } + runspace?.Close(); } } } @@ -1070,18 +1082,16 @@ public override void NotifyBeginApplication() { lock (hostGlobalLock) { - ++_beginApplicationNotifyCount; - if (_beginApplicationNotifyCount == 1) + if (++_beginApplicationNotifyCount == 1) { - // save the window title when first notified. - + // Save the window title when first notified. _savedWindowTitle = ui.RawUI.WindowTitle; #if !UNIX if (_initialConsoleMode != ConsoleControl.ConsoleModes.Unknown) { - var activeScreenBufferHandle = ConsoleControl.GetActiveScreenBufferHandle(); - _savedConsoleMode = ConsoleControl.GetMode(activeScreenBufferHandle); - ConsoleControl.SetMode(activeScreenBufferHandle, _initialConsoleMode); + var outputHandle = ConsoleControl.GetActiveScreenBufferHandle(); + _savedConsoleMode = ConsoleControl.GetMode(outputHandle); + ConsoleControl.SetMode(outputHandle, _initialConsoleMode); } #endif } @@ -1096,17 +1106,26 @@ public override void NotifyEndApplication() { lock (hostGlobalLock) { - Dbg.Assert(_beginApplicationNotifyCount > 0, "Not running an executable - NotifyBeginApplication was not called!"); - --_beginApplicationNotifyCount; - if (_beginApplicationNotifyCount == 0) + if (--_beginApplicationNotifyCount == 0) { - // restore the window title when the last application started has ended. - + // Restore the window title when the last application started has ended. ui.RawUI.WindowTitle = _savedWindowTitle; #if !UNIX if (_savedConsoleMode != ConsoleControl.ConsoleModes.Unknown) { ConsoleControl.SetMode(ConsoleControl.GetActiveScreenBufferHandle(), _savedConsoleMode); + if (_savedConsoleMode.HasFlag(ConsoleControl.ConsoleModes.VirtualTerminal)) + { + // If the console output mode we just set already has 'VirtualTerminal' turned on, + // we don't need to try turn on the VT mode separately. + return; + } + } + + if (ui.SupportsVirtualTerminal) + { + // Re-enable VT mode if it was previously enabled, as a native command may have turned it off. + ui.TryTurnOnVirtualTerminal(); } #endif } @@ -1241,15 +1260,8 @@ private void Dispose(bool isDisposingNotFinalizing) StopTranscribing(); } - if (_outputSerializer != null) - { - _outputSerializer.End(); - } - - if (_errorSerializer != null) - { - _errorSerializer.End(); - } + _outputSerializer?.End(); + _errorSerializer?.End(); if (_runspaceRef != null) { @@ -1365,14 +1377,11 @@ internal WrappedSerializer OutputSerializer { get { - if (_outputSerializer == null) - { - _outputSerializer = - new WrappedSerializer( - OutputFormat, - "Output", - Console.IsOutputRedirected ? Console.Out : ConsoleTextWriter); - } + _outputSerializer ??= + new WrappedSerializer( + OutputFormat, + "Output", + Console.IsOutputRedirected ? Console.Out : ConsoleTextWriter); return _outputSerializer; } @@ -1382,14 +1391,11 @@ internal WrappedSerializer ErrorSerializer { get { - if (_errorSerializer == null) - { - _errorSerializer = - new WrappedSerializer( - ErrorFormat, - "Error", - Console.IsErrorRedirected ? Console.Error : ConsoleTextWriter); - } + _errorSerializer ??= + new WrappedSerializer( + ErrorFormat, + "Error", + Console.IsErrorRedirected ? Console.Error : ConsoleTextWriter); return _errorSerializer; } @@ -1481,7 +1487,7 @@ private uint Run(CommandLineParameterParser cpp, bool isPrestartWarned) // NTRAID#Windows Out Of Band Releases-915506-2005/09/09 // Removed HandleUnexpectedExceptions infrastructure - exitCode = DoRunspaceLoop(cpp.InitialCommand, cpp.SkipProfiles, cpp.Args, cpp.StaMode, cpp.ConfigurationName); + exitCode = DoRunspaceLoop(cpp.InitialCommand, cpp.SkipProfiles, cpp.Args, cpp.StaMode, cpp.ConfigurationName, cpp.ConfigurationFile); } while (false); @@ -1494,13 +1500,19 @@ private uint Run(CommandLineParameterParser cpp, bool isPrestartWarned) /// /// The process exit code to be returned by Main. /// - private uint DoRunspaceLoop(string initialCommand, bool skipProfiles, Collection initialCommandArgs, bool staMode, string configurationName) + private uint DoRunspaceLoop( + string initialCommand, + bool skipProfiles, + Collection initialCommandArgs, + bool staMode, + string configurationName, + string configurationFilePath) { ExitCode = ExitCodeSuccess; while (!ShouldEndSession) { - RunspaceCreationEventArgs args = new RunspaceCreationEventArgs(initialCommand, skipProfiles, staMode, configurationName, initialCommandArgs); + RunspaceCreationEventArgs args = new RunspaceCreationEventArgs(initialCommand, skipProfiles, staMode, configurationName, configurationFilePath, initialCommandArgs); CreateRunspace(args); if (ExitCode == ExitCodeInitFailure) { break; } @@ -1576,14 +1588,12 @@ private Exception InitializeRunspaceHelper(string command, Executor exec, Execut return e; } - private void CreateRunspace(object runspaceCreationArgs) + private void CreateRunspace(RunspaceCreationEventArgs runspaceCreationArgs) { - RunspaceCreationEventArgs args = null; try { - args = runspaceCreationArgs as RunspaceCreationEventArgs; - Dbg.Assert(args != null, "Event Arguments to CreateRunspace should not be null"); - DoCreateRunspace(args.InitialCommand, args.SkipProfiles, args.StaMode, args.ConfigurationName, args.InitialCommandArgs); + Dbg.Assert(runspaceCreationArgs != null, "Arguments to CreateRunspace should not be null."); + DoCreateRunspace(runspaceCreationArgs); } catch (ConsoleHostStartupException startupException) { @@ -1595,7 +1605,7 @@ private void CreateRunspace(object runspaceCreationArgs) /// /// Check if a screen reviewer utility is running. /// When a screen reader is running, we don't auto-load the PSReadLine module at startup, - /// since PSReadLine is not accessibility-firendly enough as of today. + /// since PSReadLine is not accessibility-friendly enough as of today. /// private bool IsScreenReaderActive() { @@ -1638,12 +1648,33 @@ private static bool LoadPSReadline() /// Opens and Initializes the Host's sole Runspace. Processes the startup scripts and runs any command passed on the /// command line. /// - private void DoCreateRunspace(string initialCommand, bool skipProfiles, bool staMode, string configurationName, Collection initialCommandArgs) + /// Runspace creation event arguments. + private void DoCreateRunspace(RunspaceCreationEventArgs args) { - Dbg.Assert(_runspaceRef == null, "runspace should be null"); + Dbg.Assert(_runspaceRef == null, "_runspaceRef field should be null"); Dbg.Assert(DefaultInitialSessionState != null, "DefaultInitialSessionState should not be null"); s_runspaceInitTracer.WriteLine("Calling RunspaceFactory.CreateRunspace"); + // Use session configuration file if provided. + bool customConfigurationProvided = false; + if (!string.IsNullOrEmpty(args.ConfigurationFilePath)) + { + try + { + // Replace DefaultInitialSessionState with the initial state configuration defined by the file. + DefaultInitialSessionState = InitialSessionState.CreateFromSessionConfigurationFile( + path: args.ConfigurationFilePath, + roleVerifier: null, + validateFile: true); + } + catch (Exception ex) + { + throw new ConsoleHostStartupException(ConsoleHostStrings.ShellCannotBeStarted, ex); + } + + customConfigurationProvided = true; + } + try { Runspace consoleRunspace = null; @@ -1659,7 +1690,7 @@ private void DoCreateRunspace(string initialCommand, bool skipProfiles, bool sta // powershell -command "Update-Module PSReadline" // This should work just fine as long as no other instances of PowerShell are running. ReadOnlyCollection defaultImportModulesList = null; - if (LoadPSReadline()) + if (!customConfigurationProvided && LoadPSReadline()) { if (IsScreenReaderActive()) { @@ -1674,7 +1705,7 @@ private void DoCreateRunspace(string initialCommand, bool skipProfiles, bool sta consoleRunspace = RunspaceFactory.CreateRunspace(this, DefaultInitialSessionState); try { - OpenConsoleRunspace(consoleRunspace, staMode); + OpenConsoleRunspace(consoleRunspace, args.StaMode); } catch (Exception) { @@ -1694,7 +1725,7 @@ private void DoCreateRunspace(string initialCommand, bool skipProfiles, bool sta } consoleRunspace = RunspaceFactory.CreateRunspace(this, DefaultInitialSessionState); - OpenConsoleRunspace(consoleRunspace, staMode); + OpenConsoleRunspace(consoleRunspace, args.StaMode); } Runspace.PrimaryRunspace = consoleRunspace; @@ -1726,7 +1757,7 @@ private void DoCreateRunspace(string initialCommand, bool skipProfiles, bool sta _readyForInputTimeInMS = (DateTime.Now - Process.GetCurrentProcess().StartTime).TotalMilliseconds; #endif - DoRunspaceInitialization(skipProfiles, initialCommand, configurationName, initialCommandArgs); + DoRunspaceInitialization(args); } private static void OpenConsoleRunspace(Runspace runspace, bool staMode) @@ -1743,7 +1774,7 @@ private static void OpenConsoleRunspace(Runspace runspace, bool staMode) runspace.Open(); } - private void DoRunspaceInitialization(bool skipProfiles, string initialCommand, string configurationName, Collection initialCommandArgs) + private void DoRunspaceInitialization(RunspaceCreationEventArgs args) { if (_runspaceRef.Runspace.Debugger != null) { @@ -1778,13 +1809,13 @@ private void DoRunspaceInitialization(bool skipProfiles, string initialCommand, } } - if (!string.IsNullOrEmpty(configurationName)) + if (!string.IsNullOrEmpty(args.ConfigurationName)) { // If an endpoint configuration is specified then create a loop-back remote runspace targeting // the endpoint and push onto runspace ref stack. Ignore profile and configuration scripts. try { - RemoteRunspace remoteRunspace = HostUtilities.CreateConfiguredRunspace(configurationName, this); + RemoteRunspace remoteRunspace = HostUtilities.CreateConfiguredRunspace(args.ConfigurationName, this); remoteRunspace.ShouldCloseOnPop = true; PushRunspace(remoteRunspace); @@ -1819,7 +1850,7 @@ private void DoRunspaceInitialization(bool skipProfiles, string initialCommand, currentUserProfile, currentUserHostSpecificProfile)); - if (!skipProfiles) + if (!args.SkipProfiles) { // Run the profiles. // Profiles are run in the following order: @@ -1837,7 +1868,7 @@ private void DoRunspaceInitialization(bool skipProfiles, string initialCommand, sw.Stop(); var profileLoadTimeInMs = sw.ElapsedMilliseconds; - if (profileLoadTimeInMs > 500 && s_cpp.ShowBanner) + if (s_cpp.ShowBanner && !s_cpp.NoProfileLoadTime && profileLoadTimeInMs > 500) { Console.Error.WriteLine(ConsoleHostStrings.SlowProfileLoadingMessage, profileLoadTimeInMs); } @@ -1879,11 +1910,11 @@ private void DoRunspaceInitialization(bool skipProfiles, string initialCommand, tempPipeline.Commands.Add(c); - if (initialCommandArgs != null) + if (args.InitialCommandArgs != null) { // add the args passed to the command. - foreach (CommandParameter p in initialCommandArgs) + foreach (CommandParameter p in args.InitialCommandArgs) { c.Parameters.Add(p); } @@ -1940,19 +1971,19 @@ private void DoRunspaceInitialization(bool skipProfiles, string initialCommand, ReportException(e1, exec); } } - else if (!string.IsNullOrEmpty(initialCommand)) + else if (!string.IsNullOrEmpty(args.InitialCommand)) { // Run the command passed on the command line s_tracer.WriteLine("running initial command"); - Pipeline tempPipeline = exec.CreatePipeline(initialCommand, true); + Pipeline tempPipeline = exec.CreatePipeline(args.InitialCommand, true); - if (initialCommandArgs != null) + if (args.InitialCommandArgs != null) { // add the args passed to the command. - foreach (CommandParameter p in initialCommandArgs) + foreach (CommandParameter p in args.InitialCommandArgs) { tempPipeline.Commands[0].Parameters.Add(p); } @@ -1968,7 +1999,7 @@ private void DoRunspaceInitialization(bool skipProfiles, string initialCommand, ParseError[] errors; // Detect if they're using input. If so, read from it. - Ast parsedInput = Parser.ParseInput(initialCommand, out tokens, out errors); + Ast parsedInput = Parser.ParseInput(args.InitialCommand, out tokens, out errors); if (AstSearcher.IsUsingDollarInput(parsedInput)) { executionOptions |= Executor.ExecutionOptions.ReadInputObjects; @@ -2189,10 +2220,7 @@ private void OnExecutionSuspended(object sender, DebuggerStopEventArgs e) // For remote debugging block data coming from the main (not-nested) // running command. baseLoop = InputLoop.GetNonNestedLoop(); - if (baseLoop != null) - { - baseLoop.BlockCommandOutput(); - } + baseLoop?.BlockCommandOutput(); } // @@ -2237,10 +2265,7 @@ private void OnExecutionSuspended(object sender, DebuggerStopEventArgs e) finally { _debuggerStopEventArgs = null; - if (baseLoop != null) - { - baseLoop.ResumeCommandOutput(); - } + baseLoop?.ResumeCommandOutput(); } } @@ -2451,14 +2476,6 @@ internal void Run(bool inputLoopIsNested) while (!_parent.ShouldEndSession && !_shouldExit) { -#if !UNIX - if (ui.SupportsVirtualTerminal) - { - // need to re-enable VT mode if it was previously enabled as native commands may have turned it off - ui.TryTurnOnVtMode(); - } -#endif - try { _parent._isRunningPromptLoop = true; @@ -2493,10 +2510,7 @@ internal void Run(bool inputLoopIsNested) prompt = EvaluateDebugPrompt(); } - if (prompt == null) - { - prompt = EvaluatePrompt(); - } + prompt ??= EvaluatePrompt(); } ui.Write(prompt); @@ -2637,10 +2651,7 @@ e is RemoteException || bht = _parent._breakHandlerThread; } - if (bht != null) - { - bht.Join(); - } + bht?.Join(); // Once the pipeline has been executed, we toss any outstanding progress data and // take down the display. @@ -2833,8 +2844,7 @@ private void EvaluateSuggestions(ConsoleHostUserInterface ui) private string EvaluatePrompt() { - Exception unused = null; - string promptString = _promptExec.ExecuteCommandAndGetResultAsString("prompt", out unused); + string promptString = _promptExec.ExecuteCommandAndGetResultAsString("prompt", out _); if (string.IsNullOrEmpty(promptString)) { @@ -3021,12 +3031,14 @@ internal RunspaceCreationEventArgs( bool skipProfiles, bool staMode, string configurationName, + string configurationFilePath, Collection initialCommandArgs) { InitialCommand = initialCommand; SkipProfiles = skipProfiles; StaMode = staMode; ConfigurationName = configurationName; + ConfigurationFilePath = configurationFilePath; InitialCommandArgs = initialCommandArgs; } @@ -3038,6 +3050,8 @@ internal RunspaceCreationEventArgs( internal string ConfigurationName { get; set; } + internal string ConfigurationFilePath { get; set; } + internal Collection InitialCommandArgs { get; set; } } } // namespace diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs index 41053ecea7a..4db0882221c 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs @@ -85,9 +85,8 @@ public override GetBufferInfo(out bufferInfo); ConsoleColor foreground; - ConsoleColor unused; - ConsoleControl.WORDToColor(bufferInfo.Attributes, out foreground, out unused); + ConsoleControl.WORDToColor(bufferInfo.Attributes, out foreground, out _); return foreground; } @@ -135,9 +134,8 @@ public override GetBufferInfo(out bufferInfo); ConsoleColor background; - ConsoleColor unused; - ConsoleControl.WORDToColor(bufferInfo.Attributes, out unused, out background); + ConsoleControl.WORDToColor(bufferInfo.Attributes, out _, out background); return background; } diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs index ad20eadf3d8..16c83827221 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs @@ -87,11 +87,11 @@ internal ConsoleHostUserInterface(ConsoleHost parent) if (SupportsVirtualTerminal) { - SupportsVirtualTerminal = TryTurnOnVtMode(); + SupportsVirtualTerminal = TryTurnOnVirtualTerminal(); } } - internal bool TryTurnOnVtMode() + internal bool TryTurnOnVirtualTerminal() { #if UNIX return true; @@ -99,16 +99,22 @@ internal bool TryTurnOnVtMode() try { // Turn on virtual terminal if possible. - // This might throw - not sure how exactly (no console), but if it does, we shouldn't fail to start. - var handle = ConsoleControl.GetActiveScreenBufferHandle(); - var m = ConsoleControl.GetMode(handle); - if (ConsoleControl.NativeMethods.SetConsoleMode(handle.DangerousGetHandle(), (uint)(m | ConsoleControl.ConsoleModes.VirtualTerminal))) + var outputHandle = ConsoleControl.GetActiveScreenBufferHandle(); + var outputMode = ConsoleControl.GetMode(outputHandle); + + if (outputMode.HasFlag(ConsoleControl.ConsoleModes.VirtualTerminal)) + { + return true; + } + + outputMode |= ConsoleControl.ConsoleModes.VirtualTerminal; + if (ConsoleControl.NativeMethods.SetConsoleMode(outputHandle.DangerousGetHandle(), (uint)outputMode)) { // We only know if vt100 is supported if the previous call actually set the new flag, older // systems ignore the setting. - m = ConsoleControl.GetMode(handle); - return (m & ConsoleControl.ConsoleModes.VirtualTerminal) != 0; + outputMode = ConsoleControl.GetMode(outputHandle); + return outputMode.HasFlag(ConsoleControl.ConsoleModes.VirtualTerminal); } } catch @@ -201,9 +207,8 @@ public override string ReadLine() HandleThrowOnReadAndPrompt(); // call our internal version such that it does not end input on a tab - ReadLineResult unused; - return ReadLine(false, string.Empty, out unused, true, true); + return ReadLine(false, string.Empty, out _, true, true); } /// @@ -729,7 +734,7 @@ private void WriteImpl(string value, bool newLine) } TextWriter writer = Console.IsOutputRedirected ? Console.Out : _parent.ConsoleTextWriter; - value = GetOutputString(value, SupportsVirtualTerminal, Console.IsOutputRedirected); + value = GetOutputString(value, SupportsVirtualTerminal); if (_parent.IsRunningAsync) { @@ -1203,8 +1208,7 @@ internal string WrapToCurrentWindowWidth(string text) public override void WriteDebugLine(string message) { // don't lock here as WriteLine is already protected. - bool unused; - message = HostUtilities.RemoveGuidFromMessage(message, out unused); + message = HostUtilities.RemoveGuidFromMessage(message, out _); // We should write debug to error stream only if debug is redirected.) if (_parent.ErrorFormat == Serialization.DataFormat.XML) @@ -1215,7 +1219,7 @@ public override void WriteDebugLine(string message) { if (SupportsVirtualTerminal) { - WriteLine(GetFormatStyleString(FormatStyle.Debug, Console.IsOutputRedirected) + StringUtil.Format(ConsoleHostUserInterfaceStrings.DebugFormatString, message) + PSStyle.Instance.Reset); + WriteLine(GetFormatStyleString(FormatStyle.Debug) + StringUtil.Format(ConsoleHostUserInterfaceStrings.DebugFormatString, message) + PSStyle.Instance.Reset); } else { @@ -1264,8 +1268,7 @@ public override void WriteInformation(InformationRecord record) public override void WriteVerboseLine(string message) { // don't lock here as WriteLine is already protected. - bool unused; - message = HostUtilities.RemoveGuidFromMessage(message, out unused); + message = HostUtilities.RemoveGuidFromMessage(message, out _); // NTRAID#Windows OS Bugs-1061752-2004/12/15-sburns should read a skin setting here...) if (_parent.ErrorFormat == Serialization.DataFormat.XML) @@ -1276,7 +1279,7 @@ public override void WriteVerboseLine(string message) { if (SupportsVirtualTerminal) { - WriteLine(GetFormatStyleString(FormatStyle.Verbose, Console.IsOutputRedirected) + StringUtil.Format(ConsoleHostUserInterfaceStrings.VerboseFormatString, message) + PSStyle.Instance.Reset); + WriteLine(GetFormatStyleString(FormatStyle.Verbose) + StringUtil.Format(ConsoleHostUserInterfaceStrings.VerboseFormatString, message) + PSStyle.Instance.Reset); } else { @@ -1308,8 +1311,7 @@ public override void WriteVerboseLine(string message) public override void WriteWarningLine(string message) { // don't lock here as WriteLine is already protected. - bool unused; - message = HostUtilities.RemoveGuidFromMessage(message, out unused); + message = HostUtilities.RemoveGuidFromMessage(message, out _); // NTRAID#Windows OS Bugs-1061752-2004/12/15-sburns should read a skin setting here...) if (_parent.ErrorFormat == Serialization.DataFormat.XML) @@ -1320,7 +1322,7 @@ public override void WriteWarningLine(string message) { if (SupportsVirtualTerminal) { - WriteLine(GetFormatStyleString(FormatStyle.Warning, Console.IsOutputRedirected) + StringUtil.Format(ConsoleHostUserInterfaceStrings.WarningFormatString, message) + PSStyle.Instance.Reset); + WriteLine(GetFormatStyleString(FormatStyle.Warning) + StringUtil.Format(ConsoleHostUserInterfaceStrings.WarningFormatString, message) + PSStyle.Instance.Reset); } else { diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceProgress.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceProgress.cs index ff0117c6e99..1c9d37ea9fb 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceProgress.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceProgress.cs @@ -3,6 +3,7 @@ using System; using System.Management.Automation; +using System.Management.Automation.Host; using System.Threading; using Dbg = System.Management.Automation.Diagnostics; @@ -10,7 +11,7 @@ namespace Microsoft.PowerShell { internal partial - class ConsoleHostUserInterface : System.Management.Automation.Host.PSHostUserInterface + class ConsoleHostUserInterface : PSHostUserInterface { /// /// Called at the end of a prompt loop to take down any progress display that might have appeared and purge any @@ -48,7 +49,7 @@ class ConsoleHostUserInterface : System.Management.Automation.Host.PSHostUserInt _pendingProgress = null; - if (SupportsVirtualTerminal && PSStyle.Instance.Progress.UseOSCIndicator) + if (SupportsVirtualTerminal && !PSHost.IsStdOutputRedirected && PSStyle.Instance.Progress.UseOSCIndicator) { // OSC sequence to turn off progress indicator // https://github.com/microsoft/terminal/issues/6700 @@ -99,7 +100,7 @@ class ConsoleHostUserInterface : System.Management.Automation.Host.PSHostUserInt { // Update the progress pane only when the timer set up the update flag or WriteProgress is completed. // As a result, we do not block WriteProgress and whole script and eliminate unnecessary console locks and updates. - if (SupportsVirtualTerminal && PSStyle.Instance.Progress.UseOSCIndicator) + if (SupportsVirtualTerminal && !PSHost.IsStdOutputRedirected && PSStyle.Instance.Progress.UseOSCIndicator) { int percentComplete = record.PercentComplete; if (percentComplete < 0) @@ -138,20 +139,14 @@ class ConsoleHostUserInterface : System.Management.Automation.Host.PSHostUserInt void PreWrite() { - if (_progPane != null) - { - _progPane.Hide(); - } + _progPane?.Hide(); } private void PostWrite() { - if (_progPane != null) - { - _progPane.Show(); - } + _progPane?.Show(); } private @@ -177,20 +172,14 @@ class ConsoleHostUserInterface : System.Management.Automation.Host.PSHostUserInt void PreRead() { - if (_progPane != null) - { - _progPane.Hide(); - } + _progPane?.Hide(); } private void PostRead() { - if (_progPane != null) - { - _progPane.Show(); - } + _progPane?.Show(); } private diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleShell.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleShell.cs index 11dd276a52d..44f29311622 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleShell.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleShell.cs @@ -21,7 +21,12 @@ public static class ConsoleShell /// An integer value which should be used as exit code for the process. public static int Start(string? bannerText, string? helpText, string[] args) { - return Start(InitialSessionState.CreateDefault2(), bannerText, helpText, args); + return StartImpl( + initialSessionState: InitialSessionState.CreateDefault2(), + bannerText, + helpText, + args, + issProvided: false); } /// Entry point in to ConsoleShell. Used to create a custom Powershell console application. @@ -31,6 +36,31 @@ public static int Start(string? bannerText, string? helpText, string[] args) /// Commandline parameters specified by user. /// An integer value which should be used as exit code for the process. public static int Start(InitialSessionState initialSessionState, string? bannerText, string? helpText, string[] args) + { + return StartImpl( + initialSessionState, + bannerText, + helpText, + args, + issProvided: true); + } + + /// + /// Implementation of entry point to ConsoleShell. + /// Used to create a custom Powershell console application. + /// + /// InitialSessionState to be used by the ConsoleHost. + /// Banner text to be displayed by ConsoleHost. + /// Help text for the shell. + /// Commandline parameters specified by user. + /// True when the InitialSessionState object is provided by caller. + /// An integer value which should be used as exit code for the process. + private static int StartImpl( + InitialSessionState initialSessionState, + string? bannerText, + string? helpText, + string[] args, + bool issProvided) { if (initialSessionState == null) { @@ -45,7 +75,7 @@ public static int Start(InitialSessionState initialSessionState, string? bannerT ConsoleHost.ParseCommandLine(args); ConsoleHost.DefaultInitialSessionState = initialSessionState; - return ConsoleHost.Start(bannerText, helpText); + return ConsoleHost.Start(bannerText, helpText, issProvided); } } } diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/Executor.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/Executor.cs index 25eca0d5fd6..013281d2209 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/Executor.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/Executor.cs @@ -116,10 +116,7 @@ private void AsyncPipelineFailureHandler(Exception ex) er = new ErrorRecord(er, ex); } - if (er == null) - { - er = new ErrorRecord(ex, "ConsoleHostAsyncPipelineFailure", ErrorCategory.NotSpecified, null); - } + er ??= new ErrorRecord(ex, "ConsoleHostAsyncPipelineFailure", ErrorCategory.NotSpecified, null); _parent.ErrorSerializer.Serialize(er); } @@ -510,9 +507,8 @@ internal string ExecuteCommandAndGetResultAsString(string command, out Exception /// internal bool? ExecuteCommandAndGetResultAsBool(string command) { - Exception unused = null; - bool? result = ExecuteCommandAndGetResultAsBool(command, out unused); + bool? result = ExecuteCommandAndGetResultAsBool(command, out _); return result; } @@ -603,11 +599,9 @@ internal void BlockCommandOutput() internal void ResumeCommandOutput() { RemotePipeline remotePipeline = _pipeline as RemotePipeline; - if (remotePipeline != null) - { - // Resumes data flow. - remotePipeline.ResumeIncomingData(); - } + + // Resumes data flow. + remotePipeline?.ResumeIncomingData(); } /// @@ -701,10 +695,7 @@ internal static void CancelCurrentExecutor() temp = s_currentExecutor; } - if (temp != null) - { - temp.Cancel(); - } + temp?.Cancel(); } // These statics are threadsafe, as there can be only one instance of ConsoleHost in a process at a time, and access diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ManagedEntrance.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ManagedEntrance.cs index 6dbc21ab242..18df48d3600 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ManagedEntrance.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ManagedEntrance.cs @@ -96,7 +96,10 @@ public static int Start([MarshalAs(UnmanagedType.LPArray, ArraySubType = Unmanag ConsoleHost.DefaultInitialSessionState = InitialSessionState.CreateDefault2(); - exitCode = ConsoleHost.Start(banner, ManagedEntranceStrings.UsageHelp); + exitCode = ConsoleHost.Start( + bannerText: banner, + helpText: ManagedEntranceStrings.UsageHelp, + issProvidedExternally: false); } catch (HostException e) { diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/PendingProgress.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/PendingProgress.cs index 6bb85c64729..88aa6d1ffc9 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/PendingProgress.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/PendingProgress.cs @@ -119,10 +119,7 @@ class PendingProgress ProgressNode parentNode = FindNodeById(newNode.SourceId, newNode.ParentActivityId); if (parentNode != null) { - if (parentNode.Children == null) - { - parentNode.Children = new ArrayList(); - } + parentNode.Children ??= new ArrayList(); AddNode(parentNode.Children, newNode); break; @@ -851,18 +848,6 @@ internal override } // If we get all the way to here, then we've compressed all the nodes and we still don't fit. - -#if DEBUG || ASSERTIONS_TRACE - - Dbg.Assert( - nodesCompressed == CountNodes(), - "We should have compressed every node in the tree."); - Dbg.Assert( - AllNodesHaveGivenStyle(_topLevelNodes, newStyle), - "We should have compressed every node in the tree."); - -#endif - return false; } @@ -949,8 +934,6 @@ internal override return nodesCompressed; } - Dbg.Assert(false, "with all nodes invisible, we should never reach this point."); - return 0; } diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressPane.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressPane.cs index a3be724b7c4..404477fa6a3 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressPane.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ProgressPane.cs @@ -301,7 +301,17 @@ private void WriteContent() { if (_content is not null) { + // On Windows, we can check if the cursor is currently visible and not change it to visible + // if it is intentionally hidden. On Unix, it is not currently supported to read the cursor visibility. +#if UNIX Console.CursorVisible = false; +#else + bool currentCursorVisible = Console.CursorVisible; + if (currentCursorVisible) + { + Console.CursorVisible = false; + } +#endif var currentPosition = _rawui.CursorPosition; _rawui.CursorPosition = _location; @@ -319,7 +329,11 @@ private void WriteContent() } _rawui.CursorPosition = currentPosition; +#if UNIX Console.CursorVisible = true; +#else + Console.CursorVisible = currentCursorVisible; +#endif } } diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/Serialization.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/Serialization.cs index 4c1fb4fa4e9..5181ae63672 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/Serialization.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/Serialization.cs @@ -189,8 +189,7 @@ class WrappedDeserializer : Serialization return null; case DataFormat.XML: - string unused; - o = _xmlDeserializer.Deserialize(out unused); + o = _xmlDeserializer.Deserialize(out _); break; case DataFormat.Text: diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/UpdatesNotification.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/UpdatesNotification.cs index dff3757259f..455efe84d11 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/UpdatesNotification.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/UpdatesNotification.cs @@ -108,7 +108,7 @@ internal static void ShowUpdateNotification(PSHostUserInterface hostUI) // We calculate how much whitespace we need to make it look nice if (hostUI.SupportsVirtualTerminal) { - // Use Warning Color + // Swaps foreground and background colors. notificationColor = "\x1B[7m"; resetColor = "\x1B[0m"; @@ -126,6 +126,7 @@ internal static void ShowUpdateNotification(PSHostUserInterface hostUI) string notificationMsg = string.Format(CultureInfo.CurrentCulture, notificationMsgTemplate, releaseTag, notificationColor, resetColor, line2Padding, line3Padding); + hostUI.WriteLine(); hostUI.WriteLine(notificationMsg); } } diff --git a/src/Microsoft.PowerShell.ConsoleHost/resources/CommandLineParameterParserStrings.resx b/src/Microsoft.PowerShell.ConsoleHost/resources/CommandLineParameterParserStrings.resx index ab893b91a0a..e346331b414 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/resources/CommandLineParameterParserStrings.resx +++ b/src/Microsoft.PowerShell.ConsoleHost/resources/CommandLineParameterParserStrings.resx @@ -187,7 +187,10 @@ Valid formats are: Cannot process the command because -STA and -MTA are both specified. Specify either -STA or -MTA. - Cannot process the command because -Configuration requires an argument that is a remote endpoint configuration name. Specify this argument and try again. + Cannot process the command because -ConfigurationName requires an argument that is a remote endpoint configuration name. Specify this argument and try again. + + + Cannot process the command because -ConfigurationFile requires an argument that is a session configuration (.pssc) file path. Specify this argument and try again. Cannot process the command because -CustomPipeName requires an argument that is a name of the pipe you want to use. Specify this argument and try again. diff --git a/src/Microsoft.PowerShell.ConsoleHost/resources/ConsoleHostStrings.resx b/src/Microsoft.PowerShell.ConsoleHost/resources/ConsoleHostStrings.resx index 7296bb30feb..49c22783c1f 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/resources/ConsoleHostStrings.resx +++ b/src/Microsoft.PowerShell.ConsoleHost/resources/ConsoleHostStrings.resx @@ -135,6 +135,9 @@ The shell cannot be started. A failure occurred during initialization: + + The shell cannot be started. An InitialSessionState object has been provided along with a -ConfigurationFile argument. Both configuration directives cannot be used at the same time. + An error has occurred that was not properly handled. Additional information is shown below. The PowerShell process will exit. diff --git a/src/Microsoft.PowerShell.ConsoleHost/resources/ManagedEntranceStrings.resx b/src/Microsoft.PowerShell.ConsoleHost/resources/ManagedEntranceStrings.resx index 7b13837b294..15cd09e7de0 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/resources/ManagedEntranceStrings.resx +++ b/src/Microsoft.PowerShell.ConsoleHost/resources/ManagedEntranceStrings.resx @@ -118,11 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - PowerShell {0} -Copyright (c) Microsoft Corporation. - -https://aka.ms/powershell -Type 'help' to get help. + PowerShell {0} Warning: PowerShell detected that you might be using a screen reader and has disabled PSReadLine for compatibility purposes. If you want to re-enable it, run 'Import-Module PSReadLine'. @@ -149,12 +145,14 @@ Type 'help' to get help. Usage: pwsh[.exe] [-Login] [[-File] <filePath> [args]] [-Command { - | <script-block> [-args <arg-array>] | <string> [<CommandParameters>] } ] - [-ConfigurationName <string>] [-CustomPipeName <string>] - [-EncodedCommand <Base64EncodedCommand>] + [-ConfigurationName <string>] [-ConfigurationFile <filePath>] + [-CustomPipeName <string>] [-EncodedCommand <Base64EncodedCommand>] [-ExecutionPolicy <ExecutionPolicy>] [-InputFormat {Text | XML}] [-Interactive] [-MTA] [-NoExit] [-NoLogo] [-NonInteractive] [-NoProfile] - [-OutputFormat {Text | XML}] [-SettingsFile <filePath>] [-SSHServerMode] [-STA] - [-Version] [-WindowStyle <style>] [-WorkingDirectory <directoryPath>] + [-NoProfileLoadTime] [-OutputFormat {Text | XML}] + [-SettingsFile <filePath>] [-SSHServerMode] [-STA] + [-Version] [-WindowStyle <style>] + [-WorkingDirectory <directoryPath>] pwsh[.exe] -h | -Help | -? | /? @@ -292,6 +290,14 @@ All parameters are case-insensitive. Example: "pwsh -ConfigurationName AdminRoles" +-ConfigurationFile + + Specifies a session configuration (.pssc) file path. The configuration + contained in the configuration file will be applied to the PowerShell + session. + + Example: "pwsh -ConfigurationFile "C:\ProgramData\PowerShell\MyConfig.pssc" + -CustomPipeName Specifies the name to use for an additional IPC server (named pipe) used @@ -383,7 +389,7 @@ All parameters are case-insensitive. -NoLogo | -nol - Hides the copyright banner at startup of interactive sessions. + Hides the banner text at startup of interactive sessions. -NonInteractive | -noni @@ -395,6 +401,11 @@ All parameters are case-insensitive. Does not load the PowerShell profiles. +-NoProfileLoadTime + + Hides the PowerShell profile load time text shown at startup when the load + time exceeds 500 milliseconds. + -OutputFormat | -o | -of Determines how output from PowerShell is formatted. Valid values are "Text" diff --git a/src/Microsoft.PowerShell.CoreCLR.Eventing/Microsoft.PowerShell.CoreCLR.Eventing.csproj b/src/Microsoft.PowerShell.CoreCLR.Eventing/Microsoft.PowerShell.CoreCLR.Eventing.csproj index e30019fac81..26376c5609f 100644 --- a/src/Microsoft.PowerShell.CoreCLR.Eventing/Microsoft.PowerShell.CoreCLR.Eventing.csproj +++ b/src/Microsoft.PowerShell.CoreCLR.Eventing/Microsoft.PowerShell.CoreCLR.Eventing.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Microsoft.PowerShell.GlobalTool.Shim/GlobalToolShim.cs b/src/Microsoft.PowerShell.GlobalTool.Shim/GlobalToolShim.cs index 3b253582b63..c8e95628687 100644 --- a/src/Microsoft.PowerShell.GlobalTool.Shim/GlobalToolShim.cs +++ b/src/Microsoft.PowerShell.GlobalTool.Shim/GlobalToolShim.cs @@ -26,7 +26,7 @@ public static class EntryPoint public static int Main(string[] args) { var currentPath = new FileInfo(System.Reflection.Assembly.GetEntryAssembly().Location).Directory.FullName; - var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + var isWindows = OperatingSystem.IsWindows(); string platformFolder = isWindows ? WinFolderName : UnixFolderName; diff --git a/src/Microsoft.PowerShell.GlobalTool.Shim/Microsoft.PowerShell.GlobalTool.Shim.csproj b/src/Microsoft.PowerShell.GlobalTool.Shim/Microsoft.PowerShell.GlobalTool.Shim.csproj index aa845a7817d..d0203344cc2 100644 --- a/src/Microsoft.PowerShell.GlobalTool.Shim/Microsoft.PowerShell.GlobalTool.Shim.csproj +++ b/src/Microsoft.PowerShell.GlobalTool.Shim/Microsoft.PowerShell.GlobalTool.Shim.csproj @@ -6,6 +6,7 @@ Microsoft.PowerShell.GlobalTool.Shim EXE Microsoft.PowerShell.GlobalTool.Shim + False diff --git a/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj b/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj index 4b8485ac58b..69084ba0828 100644 --- a/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj +++ b/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj @@ -15,22 +15,28 @@ + + + + + + + - + - - - + + + - - - - - - + + + + + + - - + diff --git a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobStore.cs b/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobStore.cs index bd9d7439696..0938d9829b9 100644 --- a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobStore.cs +++ b/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobStore.cs @@ -115,7 +115,7 @@ public static FileStream GetFileForJobDefinition( } /// - /// Checks the provided path against the the default path of scheduled jobs + /// Checks the provided path against the default path of scheduled jobs /// for the current user. /// /// Path for scheduled job definitions. diff --git a/src/Microsoft.PowerShell.Security/resources/SignatureCommands.resx b/src/Microsoft.PowerShell.Security/resources/SignatureCommands.resx index 53f5371fc4c..cbbbd5e5712 100644 --- a/src/Microsoft.PowerShell.Security/resources/SignatureCommands.resx +++ b/src/Microsoft.PowerShell.Security/resources/SignatureCommands.resx @@ -121,7 +121,7 @@ Cannot sign code. The specified certificate is not suitable for code signing. - Cannot sign code. The TimeStamp server URL must be fully qualified in the form of http://<server url> + Cannot sign code. The TimeStamp server URL must be fully qualified in the form of http://<server url> or https://<server url>. The Get-AuthenticodeSignature cmdlet does not support directories. Supply a path to a file and retry. diff --git a/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs b/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs index 1292ecb9b55..3a367283828 100644 --- a/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs +++ b/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs @@ -26,7 +26,7 @@ using Dbg = System.Management.Automation; using DWORD = System.UInt32; using Runspaces = System.Management.Automation.Runspaces; -using Security = System.Management.Automation.Security; +using SMASecurity = System.Management.Automation.Security; namespace Microsoft.PowerShell.Commands { @@ -275,7 +275,7 @@ protected override bool ReleaseHandle() if (handle != IntPtr.Zero) { - fResult = Security.NativeMethods.CertCloseStore(handle, 0); + fResult = SMASecurity.NativeMethods.CertCloseStore(handle, 0); handle = IntPtr.Zero; } @@ -318,25 +318,25 @@ public void Open(bool includeArchivedCerts) _valid = false; _open = false; - Security.NativeMethods.CertOpenStoreFlags StoreFlags = - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_SHARE_STORE_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_SHARE_CONTEXT_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_OPEN_EXISTING_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_MAXIMUM_ALLOWED_FLAG; + SMASecurity.NativeMethods.CertOpenStoreFlags StoreFlags = + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_SHARE_STORE_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_SHARE_CONTEXT_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_OPEN_EXISTING_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_MAXIMUM_ALLOWED_FLAG; if (includeArchivedCerts) { - StoreFlags |= Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_ENUM_ARCHIVED_FLAG; + StoreFlags |= SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_ENUM_ARCHIVED_FLAG; } switch (_storeLocation.Location) { case StoreLocation.LocalMachine: - StoreFlags |= Security.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; + StoreFlags |= SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; break; case StoreLocation.CurrentUser: - StoreFlags |= Security.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER; + StoreFlags |= SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER; break; default: @@ -344,9 +344,9 @@ public void Open(bool includeArchivedCerts) break; } - IntPtr hCertStore = Security.NativeMethods.CertOpenStore( - Security.NativeMethods.CertOpenStoreProvider.CERT_STORE_PROV_SYSTEM, - Security.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, + IntPtr hCertStore = SMASecurity.NativeMethods.CertOpenStore( + SMASecurity.NativeMethods.CertOpenStoreProvider.CERT_STORE_PROV_SYSTEM, + SMASecurity.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, IntPtr.Zero, // hCryptProv StoreFlags, _storeName); @@ -364,10 +364,10 @@ public void Open(bool includeArchivedCerts) "UserDS", StringComparison.OrdinalIgnoreCase)) { - if (!Security.NativeMethods.CertControlStore( + if (!SMASecurity.NativeMethods.CertControlStore( _storeHandle.Handle, 0, - Security.NativeMethods.CertControlStoreType.CERT_STORE_CTRL_AUTO_RESYNC, + SMASecurity.NativeMethods.CertControlStoreType.CERT_STORE_CTRL_AUTO_RESYNC, IntPtr.Zero)) { _storeHandle = null; @@ -391,12 +391,12 @@ public IntPtr GetNextCert(IntPtr certContext) if (!_open) { throw Marshal.GetExceptionForHR( - Security.NativeMethods.CRYPT_E_NOT_FOUND); + SMASecurity.NativeMethods.CRYPT_E_NOT_FOUND); } if (Valid) { - certContext = Security.NativeMethods.CertEnumCertificatesInStore( + certContext = SMASecurity.NativeMethods.CertEnumCertificatesInStore( _storeHandle.Handle, certContext); } @@ -415,18 +415,18 @@ public IntPtr GetCertByName(string Name) if (!_open) { throw Marshal.GetExceptionForHR( - Security.NativeMethods.CRYPT_E_NOT_FOUND); + SMASecurity.NativeMethods.CRYPT_E_NOT_FOUND); } if (Valid) { if (DownLevelHelper.HashLookupSupported()) { - certContext = Security.NativeMethods.CertFindCertificateInStore( + certContext = SMASecurity.NativeMethods.CertFindCertificateInStore( _storeHandle.Handle, - Security.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, + SMASecurity.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, 0, // dwFindFlags - Security.NativeMethods.CertFindType.CERT_FIND_HASH_STR, + SMASecurity.NativeMethods.CertFindType.CERT_FIND_HASH_STR, Name, IntPtr.Zero); // pPrevCertContext } @@ -464,7 +464,7 @@ public IntPtr GetCertByName(string Name) public void FreeCert(IntPtr certContext) { - Security.NativeMethods.CertFreeCertificateContext(certContext); + SMASecurity.NativeMethods.CertFreeCertificateContext(certContext); } /// @@ -556,6 +556,7 @@ internal enum CertificateProviderItem [CmdletProvider("Certificate", ProviderCapabilities.ShouldProcess)] [OutputType(typeof(string), typeof(PathInfo), ProviderCmdlet = ProviderCmdlet.ResolvePath)] [OutputType(typeof(PathInfo), ProviderCmdlet = ProviderCmdlet.PushLocation)] + [OutputType(typeof(PathInfo), ProviderCmdlet = ProviderCmdlet.PopLocation)] [OutputType(typeof(Microsoft.PowerShell.Commands.X509StoreLocation), typeof(X509Certificate2), ProviderCmdlet = ProviderCmdlet.GetItem)] [OutputType(typeof(X509Store), typeof(X509Certificate2), ProviderCmdlet = ProviderCmdlet.GetChildItem)] public sealed class CertificateProvider : NavigationCmdletProvider, ICmdletProviderSupportsHelp @@ -973,15 +974,15 @@ protected override void NewItem( ThrowInvalidOperation(errorId, message); } - const Security.NativeMethods.CertOpenStoreFlags StoreFlags = - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_CREATE_NEW_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_MAXIMUM_ALLOWED_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; + const SMASecurity.NativeMethods.CertOpenStoreFlags StoreFlags = + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_CREATE_NEW_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_MAXIMUM_ALLOWED_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; // Create new store - IntPtr hCertStore = Security.NativeMethods.CertOpenStore( - Security.NativeMethods.CertOpenStoreProvider.CERT_STORE_PROV_SYSTEM, - Security.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, + IntPtr hCertStore = SMASecurity.NativeMethods.CertOpenStore( + SMASecurity.NativeMethods.CertOpenStoreProvider.CERT_STORE_PROV_SYSTEM, + SMASecurity.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, IntPtr.Zero, // hCryptProv StoreFlags, pathElements[1]); @@ -992,7 +993,7 @@ protected override void NewItem( else // free native store handle { bool fResult = false; - fResult = Security.NativeMethods.CertCloseStore(hCertStore, 0); + fResult = SMASecurity.NativeMethods.CertCloseStore(hCertStore, 0); } X509Store outStore = new(pathElements[1], StoreLocation.LocalMachine); @@ -1615,8 +1616,8 @@ private static string[] GetPathElements(string path) private void DoDeleteKey(IntPtr pProvInfo) { IntPtr hProv = IntPtr.Zero; - Security.NativeMethods.CRYPT_KEY_PROV_INFO keyProvInfo = - Marshal.PtrToStructure(pProvInfo); + SMASecurity.NativeMethods.CRYPT_KEY_PROV_INFO keyProvInfo = + Marshal.PtrToStructure(pProvInfo); IntPtr hWnd = DetectUIHelper.GetOwnerWindow(Host); @@ -1624,33 +1625,33 @@ private void DoDeleteKey(IntPtr pProvInfo) { if (hWnd != IntPtr.Zero) { - if (Security.NativeMethods.CryptAcquireContext( + if (SMASecurity.NativeMethods.CryptAcquireContext( ref hProv, keyProvInfo.pwszContainerName, keyProvInfo.pwszProvName, (int)keyProvInfo.dwProvType, - (uint)Security.NativeMethods.ProviderFlagsEnum.CRYPT_VERIFYCONTEXT)) + (uint)SMASecurity.NativeMethods.ProviderFlagsEnum.CRYPT_VERIFYCONTEXT)) { unsafe { void* pWnd = hWnd.ToPointer(); - Security.NativeMethods.CryptSetProvParam( + SMASecurity.NativeMethods.CryptSetProvParam( hProv, - Security.NativeMethods.ProviderParam.PP_CLIENT_HWND, + SMASecurity.NativeMethods.ProviderParam.PP_CLIENT_HWND, &pWnd, 0); - Security.NativeMethods.CryptReleaseContext(hProv, 0); + SMASecurity.NativeMethods.CryptReleaseContext(hProv, 0); } } } - if (!Security.NativeMethods.CryptAcquireContext( + if (!SMASecurity.NativeMethods.CryptAcquireContext( ref hProv, keyProvInfo.pwszContainerName, keyProvInfo.pwszProvName, (int)keyProvInfo.dwProvType, - keyProvInfo.dwFlags | (uint)Security.NativeMethods.ProviderFlagsEnum.CRYPT_DELETEKEYSET | - (hWnd == IntPtr.Zero ? (uint)Security.NativeMethods.ProviderFlagsEnum.CRYPT_SILENT : 0))) + keyProvInfo.dwFlags | (uint)SMASecurity.NativeMethods.ProviderFlagsEnum.CRYPT_DELETEKEYSET | + (hWnd == IntPtr.Zero ? (uint)SMASecurity.NativeMethods.ProviderFlagsEnum.CRYPT_SILENT : 0))) { ThrowErrorRemoting(Marshal.GetLastWin32Error()); } @@ -1663,21 +1664,21 @@ private void DoDeleteKey(IntPtr pProvInfo) IntPtr hCNGProv = IntPtr.Zero; IntPtr hCNGKey = IntPtr.Zero; - if ((keyProvInfo.dwFlags & (uint)Security.NativeMethods.ProviderFlagsEnum.CRYPT_MACHINE_KEYSET) != 0) + if ((keyProvInfo.dwFlags & (uint)SMASecurity.NativeMethods.ProviderFlagsEnum.CRYPT_MACHINE_KEYSET) != 0) { - cngKeyFlag = (uint)Security.NativeMethods.NCryptDeletKeyFlag.NCRYPT_MACHINE_KEY_FLAG; + cngKeyFlag = (uint)SMASecurity.NativeMethods.NCryptDeletKeyFlag.NCRYPT_MACHINE_KEY_FLAG; } if (hWnd == IntPtr.Zero || - (keyProvInfo.dwFlags & (uint)Security.NativeMethods.ProviderFlagsEnum.CRYPT_SILENT) != 0) + (keyProvInfo.dwFlags & (uint)SMASecurity.NativeMethods.ProviderFlagsEnum.CRYPT_SILENT) != 0) { - cngKeyFlag |= (uint)Security.NativeMethods.NCryptDeletKeyFlag.NCRYPT_SILENT_FLAG; + cngKeyFlag |= (uint)SMASecurity.NativeMethods.NCryptDeletKeyFlag.NCRYPT_SILENT_FLAG; } int stat = 0; try { - stat = Security.NativeMethods.NCryptOpenStorageProvider( + stat = SMASecurity.NativeMethods.NCryptOpenStorageProvider( ref hCNGProv, keyProvInfo.pwszProvName, 0); @@ -1686,7 +1687,7 @@ private void DoDeleteKey(IntPtr pProvInfo) ThrowErrorRemoting(stat); } - stat = Security.NativeMethods.NCryptOpenKey( + stat = SMASecurity.NativeMethods.NCryptOpenKey( hCNGProv, ref hCNGKey, keyProvInfo.pwszContainerName, @@ -1697,21 +1698,21 @@ private void DoDeleteKey(IntPtr pProvInfo) ThrowErrorRemoting(stat); } - if ((cngKeyFlag & (uint)Security.NativeMethods.NCryptDeletKeyFlag.NCRYPT_SILENT_FLAG) != 0) + if ((cngKeyFlag & (uint)SMASecurity.NativeMethods.NCryptDeletKeyFlag.NCRYPT_SILENT_FLAG) != 0) { unsafe { void* pWnd = hWnd.ToPointer(); - Security.NativeMethods.NCryptSetProperty( + SMASecurity.NativeMethods.NCryptSetProperty( hCNGProv, - Security.NativeMethods.NCRYPT_WINDOW_HANDLE_PROPERTY, + SMASecurity.NativeMethods.NCRYPT_WINDOW_HANDLE_PROPERTY, &pWnd, sizeof(void*), 0); // dwFlags } } - stat = Security.NativeMethods.NCryptDeleteKey(hCNGKey, 0); + stat = SMASecurity.NativeMethods.NCryptDeleteKey(hCNGKey, 0); if (stat != 0) { ThrowErrorRemoting(stat); @@ -1722,10 +1723,10 @@ private void DoDeleteKey(IntPtr pProvInfo) finally { if (hCNGProv != IntPtr.Zero) - result = Security.NativeMethods.NCryptFreeObject(hCNGProv); + result = SMASecurity.NativeMethods.NCryptFreeObject(hCNGProv); if (hCNGKey != IntPtr.Zero) - result = Security.NativeMethods.NCryptFreeObject(hCNGKey); + result = SMASecurity.NativeMethods.NCryptFreeObject(hCNGKey); } } } @@ -1741,7 +1742,7 @@ private void DoDeleteKey(IntPtr pProvInfo) private void RemoveCertStore(string storeName, bool fDeleteKey, string sourcePath) { // if recurse is true, remove every cert in the store - IntPtr localName = Security.NativeMethods.CryptFindLocalizedName(storeName); + IntPtr localName = SMASecurity.NativeMethods.CryptFindLocalizedName(storeName); string[] pathElements = GetPathElements(sourcePath); if (localName == IntPtr.Zero)//not find, we can remove { @@ -1766,17 +1767,17 @@ private void RemoveCertStore(string storeName, bool fDeleteKey, string sourcePat certContext = store.GetNextCert(certContext); } // remove the cert store - const Security.NativeMethods.CertOpenStoreFlags StoreFlags = - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_READONLY_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_OPEN_EXISTING_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_STORE_DELETE_FLAG | - Security.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; + const SMASecurity.NativeMethods.CertOpenStoreFlags StoreFlags = + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_READONLY_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_OPEN_EXISTING_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_STORE_DELETE_FLAG | + SMASecurity.NativeMethods.CertOpenStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; // delete store - IntPtr hCertStore = Security.NativeMethods.CertOpenStore( - Security.NativeMethods.CertOpenStoreProvider.CERT_STORE_PROV_SYSTEM, - Security.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, + IntPtr hCertStore = SMASecurity.NativeMethods.CertOpenStore( + SMASecurity.NativeMethods.CertOpenStoreProvider.CERT_STORE_PROV_SYSTEM, + SMASecurity.NativeMethods.CertOpenStoreEncodingType.X509_ASN_ENCODING, IntPtr.Zero, // hCryptProv StoreFlags, storeName); @@ -1848,17 +1849,17 @@ private void DoRemove(X509Certificate2 cert, bool fDeleteKey, bool fMachine, str if (fDeleteKey) { // it is fine if below call fails - if (Security.NativeMethods.CertGetCertificateContextProperty( + if (SMASecurity.NativeMethods.CertGetCertificateContextProperty( cert.Handle, - Security.NativeMethods.CertPropertyId.CERT_KEY_PROV_INFO_PROP_ID, + SMASecurity.NativeMethods.CertPropertyId.CERT_KEY_PROV_INFO_PROP_ID, IntPtr.Zero, ref provSize)) { pProvInfo = Marshal.AllocHGlobal((int)provSize); - if (Security.NativeMethods.CertGetCertificateContextProperty( + if (SMASecurity.NativeMethods.CertGetCertificateContextProperty( cert.Handle, - Security.NativeMethods.CertPropertyId.CERT_KEY_PROV_INFO_PROP_ID, + SMASecurity.NativeMethods.CertPropertyId.CERT_KEY_PROV_INFO_PROP_ID, pProvInfo, ref provSize)) { @@ -1878,8 +1879,8 @@ private void DoRemove(X509Certificate2 cert, bool fDeleteKey, bool fMachine, str // do remove certificate // should not use the original handle - if (!Security.NativeMethods.CertDeleteCertificateFromStore( - Security.NativeMethods.CertDuplicateCertificateContext(cert.Handle))) + if (!SMASecurity.NativeMethods.CertDeleteCertificateFromStore( + SMASecurity.NativeMethods.CertDuplicateCertificateContext(cert.Handle))) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } @@ -1887,8 +1888,8 @@ private void DoRemove(X509Certificate2 cert, bool fDeleteKey, bool fMachine, str // commit the change to physical store if (sourcePath.Contains("UserDS")) { - Security.NativeMethods.CERT_CONTEXT context = - Marshal.PtrToStructure(cert.Handle); + SMASecurity.NativeMethods.CERT_CONTEXT context = + Marshal.PtrToStructure(cert.Handle); CommitUserDS(context.hCertStore); } @@ -1915,10 +1916,10 @@ private void DoRemove(X509Certificate2 cert, bool fDeleteKey, bool fMachine, str /// No return. private static void CommitUserDS(IntPtr storeHandle) { - if (!Security.NativeMethods.CertControlStore( + if (!SMASecurity.NativeMethods.CertControlStore( storeHandle, 0, - Security.NativeMethods.CertControlStoreType.CERT_STORE_CTRL_COMMIT, + SMASecurity.NativeMethods.CertControlStoreType.CERT_STORE_CTRL_COMMIT, IntPtr.Zero)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); @@ -1940,7 +1941,7 @@ private void DoMove(string destination, X509Certificate2 cert, X509NativeStore s IntPtr outCert = IntPtr.Zero; // duplicate cert first - dupCert = Security.NativeMethods.CertDuplicateCertificateContext(cert.Handle); + dupCert = SMASecurity.NativeMethods.CertDuplicateCertificateContext(cert.Handle); if (dupCert == IntPtr.Zero) { @@ -1948,16 +1949,16 @@ private void DoMove(string destination, X509Certificate2 cert, X509NativeStore s } else { - if (!Security.NativeMethods.CertAddCertificateContextToStore( + if (!SMASecurity.NativeMethods.CertAddCertificateContextToStore( store.StoreHandle, cert.Handle, - (uint)Security.NativeMethods.AddCertificateContext.CERT_STORE_ADD_ALWAYS, + (uint)SMASecurity.NativeMethods.AddCertificateContext.CERT_STORE_ADD_ALWAYS, ref outCert)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } - if (!Security.NativeMethods.CertDeleteCertificateFromStore(dupCert)) + if (!SMASecurity.NativeMethods.CertDeleteCertificateFromStore(dupCert)) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } @@ -1973,7 +1974,7 @@ private void DoMove(string destination, X509Certificate2 cert, X509NativeStore s if (sourcePath.Contains("UserDS")) { - Security.NativeMethods.CERT_CONTEXT context = Marshal.PtrToStructure(cert.Handle); + SMASecurity.NativeMethods.CERT_CONTEXT context = Marshal.PtrToStructure(cert.Handle); CommitUserDS(context.hCertStore); } @@ -2545,10 +2546,7 @@ private X509NativeStore GetStore(string storePath, } } - if (s_storeCache == null) - { - s_storeCache = new X509NativeStore(storeLocation, storeName); - } + s_storeCache ??= new X509NativeStore(storeLocation, storeName); return s_storeCache; } @@ -3151,9 +3149,9 @@ public static bool ReadSendAsTrustedIssuerProperty(X509Certificate2 cert) int propSize = 0; // try to get the property // it is fine if fail for not there - if (Security.NativeMethods.CertGetCertificateContextProperty( + if (SMASecurity.NativeMethods.CertGetCertificateContextProperty( cert.Handle, - Security.NativeMethods.CertPropertyId.CERT_SEND_AS_TRUSTED_ISSUER_PROP_ID, + SMASecurity.NativeMethods.CertPropertyId.CERT_SEND_AS_TRUSTED_ISSUER_PROP_ID, IntPtr.Zero, ref propSize)) { @@ -3164,7 +3162,7 @@ public static bool ReadSendAsTrustedIssuerProperty(X509Certificate2 cert) { // if fail int error = Marshal.GetLastWin32Error(); - if (error != Security.NativeMethods.CRYPT_E_NOT_FOUND) + if (error != SMASecurity.NativeMethods.CRYPT_E_NOT_FOUND) { throw new System.ComponentModel.Win32Exception(error); } @@ -3183,7 +3181,7 @@ public static void WriteSendAsTrustedIssuerProperty(X509Certificate2 cert, strin if (DownLevelHelper.TrustedIssuerSupported()) { IntPtr propertyPtr = IntPtr.Zero; - Security.NativeMethods.CRYPT_DATA_BLOB dataBlob = new(); + SMASecurity.NativeMethods.CRYPT_DATA_BLOB dataBlob = new(); dataBlob.cbData = 0; dataBlob.pbData = IntPtr.Zero; X509Certificate certFromStore = null; @@ -3230,9 +3228,9 @@ public static void WriteSendAsTrustedIssuerProperty(X509Certificate2 cert, strin } // set property - if (!Security.NativeMethods.CertSetCertificateContextProperty( + if (!SMASecurity.NativeMethods.CertSetCertificateContextProperty( certFromStore != null ? certFromStore.Handle : cert.Handle, - Security.NativeMethods.CertPropertyId.CERT_SEND_AS_TRUSTED_ISSUER_PROP_ID, + SMASecurity.NativeMethods.CertPropertyId.CERT_SEND_AS_TRUSTED_ISSUER_PROP_ID, 0, propertyPtr)) { @@ -3249,7 +3247,7 @@ public static void WriteSendAsTrustedIssuerProperty(X509Certificate2 cert, strin } else { - Marshal.ThrowExceptionForHR(Security.NativeMethods.NTE_NOT_SUPPORTED); + Marshal.ThrowExceptionForHR(SMASecurity.NativeMethods.NTE_NOT_SUPPORTED); } } @@ -3486,12 +3484,12 @@ internal static IntPtr GetOwnerWindow(PSHost host) if (hWnd == IntPtr.Zero) { - hWnd = Security.NativeMethods.GetConsoleWindow(); + hWnd = SMASecurity.NativeMethods.GetConsoleWindow(); } if (hWnd == IntPtr.Zero) { - hWnd = Security.NativeMethods.GetDesktopWindow(); + hWnd = SMASecurity.NativeMethods.GetDesktopWindow(); } } } @@ -3506,7 +3504,7 @@ private static bool IsUIAllowed(PSHost host) uint SessionId; uint ProcessId = (uint)System.Diagnostics.Process.GetCurrentProcess().Id; - if (!Security.NativeMethods.ProcessIdToSessionId(ProcessId, out SessionId)) + if (!SMASecurity.NativeMethods.ProcessIdToSessionId(ProcessId, out SessionId)) return false; if (SessionId == 0) @@ -3552,17 +3550,17 @@ internal static class Crypt32Helpers [ArchitectureSensitive] internal static List GetStoreNamesAtLocation(StoreLocation location) { - Security.NativeMethods.CertStoreFlags locationFlag = - Security.NativeMethods.CertStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER; + SMASecurity.NativeMethods.CertStoreFlags locationFlag = + SMASecurity.NativeMethods.CertStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER; switch (location) { case StoreLocation.CurrentUser: - locationFlag = Security.NativeMethods.CertStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER; + locationFlag = SMASecurity.NativeMethods.CertStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER; break; case StoreLocation.LocalMachine: - locationFlag = Security.NativeMethods.CertStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; + locationFlag = SMASecurity.NativeMethods.CertStoreFlags.CERT_SYSTEM_STORE_LOCAL_MACHINE; break; default: @@ -3570,7 +3568,7 @@ internal static List GetStoreNamesAtLocation(StoreLocation location) break; } - Security.NativeMethods.CertEnumSystemStoreCallBackProto callBack = new(CertEnumSystemStoreCallBack); + SMASecurity.NativeMethods.CertEnumSystemStoreCallBackProto callBack = new(CertEnumSystemStoreCallBack); // Return a new list to avoid synchronization issues. @@ -3579,7 +3577,7 @@ internal static List GetStoreNamesAtLocation(StoreLocation location) { storeNames.Clear(); - Security.NativeMethods.CertEnumSystemStore(locationFlag, IntPtr.Zero, + SMASecurity.NativeMethods.CertEnumSystemStore(locationFlag, IntPtr.Zero, IntPtr.Zero, callBack); foreach (string name in storeNames) { diff --git a/src/Microsoft.PowerShell.Security/security/SignatureCommands.cs b/src/Microsoft.PowerShell.Security/security/SignatureCommands.cs index 5d5bb06b667..411c7952ea8 100644 --- a/src/Microsoft.PowerShell.Security/security/SignatureCommands.cs +++ b/src/Microsoft.PowerShell.Security/security/SignatureCommands.cs @@ -374,10 +374,7 @@ public string TimestampServer set { - if (value == null) - { - value = string.Empty; - } + value ??= string.Empty; _timestampServer = value; } @@ -404,7 +401,7 @@ public string HashAlgorithm } } - private string _hashAlgorithm = null; + private string _hashAlgorithm = "SHA256"; /// /// Property that sets force parameter. diff --git a/src/Microsoft.WSMan.Management/CredSSP.cs b/src/Microsoft.WSMan.Management/CredSSP.cs index cd5e6d3fc4d..2470a6fc3c4 100644 --- a/src/Microsoft.WSMan.Management/CredSSP.cs +++ b/src/Microsoft.WSMan.Management/CredSSP.cs @@ -389,6 +389,7 @@ protected override void BeginProcessing() /// authentication is achieved via a trusted X509 certificate or Kerberos. /// [Cmdlet(VerbsLifecycle.Enable, "WSManCredSSP", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096719")] + [OutputType(typeof(XmlElement))] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cred")] [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SSP")] public class EnableWSManCredSSPCommand : WSManCredSSPCommandBase, IDisposable/*, IDynamicParameters*/ @@ -736,6 +737,7 @@ private void UpdateGPORegistrySettings(string applicationname, string[] delegate [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cred")] [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SSP")] [Cmdlet(VerbsCommon.Get, "WSManCredSSP", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096838")] + [OutputType(typeof(string))] public class GetWSManCredSSPCommand : PSCmdlet, IDisposable { #region private diff --git a/src/Microsoft.WSMan.Management/CurrentConfigurations.cs b/src/Microsoft.WSMan.Management/CurrentConfigurations.cs index 0129554c30b..95b8f4ba584 100644 --- a/src/Microsoft.WSMan.Management/CurrentConfigurations.cs +++ b/src/Microsoft.WSMan.Management/CurrentConfigurations.cs @@ -98,7 +98,7 @@ public bool RefreshCurrentConfiguration(string responseOfGet) /// Issues a PUT request with the ResourceUri provided. /// /// Resource URI to use. - /// False, if operation is not succesful. + /// False, if operation is not successful. public void PutConfigurationOnServer(string resourceUri) { if (string.IsNullOrEmpty(resourceUri)) diff --git a/src/Microsoft.WSMan.Management/InvokeWSManAction.cs b/src/Microsoft.WSMan.Management/InvokeWSManAction.cs index 16e91cceae9..da92a680ab3 100644 --- a/src/Microsoft.WSMan.Management/InvokeWSManAction.cs +++ b/src/Microsoft.WSMan.Management/InvokeWSManAction.cs @@ -24,6 +24,7 @@ namespace Microsoft.WSMan.Management /// -SelectorSet {Name=Spooler} /// [Cmdlet(VerbsLifecycle.Invoke, "WSManAction", DefaultParameterSetName = "URI", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096843")] + [OutputType(typeof(XmlElement))] public class InvokeWSManActionCommand : AuthenticatingWSManCommand, IDisposable { /// diff --git a/src/Microsoft.WSMan.Management/Microsoft.WSMan.Management.csproj b/src/Microsoft.WSMan.Management/Microsoft.WSMan.Management.csproj index 56c0310e2a0..94a13515348 100644 --- a/src/Microsoft.WSMan.Management/Microsoft.WSMan.Management.csproj +++ b/src/Microsoft.WSMan.Management/Microsoft.WSMan.Management.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Microsoft.WSMan.Management/NewWSManSession.cs b/src/Microsoft.WSMan.Management/NewWSManSession.cs index 25f5bdd5989..09b22af9924 100644 --- a/src/Microsoft.WSMan.Management/NewWSManSession.cs +++ b/src/Microsoft.WSMan.Management/NewWSManSession.cs @@ -26,6 +26,7 @@ namespace Microsoft.WSMan.Management /// Connect-WSMan. /// [Cmdlet(VerbsCommon.New, "WSManSessionOption", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096845")] + [OutputType(typeof(SessionOption))] public class NewWSManSessionOptionCommand : PSCmdlet { /// diff --git a/src/Microsoft.WSMan.Management/PingWSMan.cs b/src/Microsoft.WSMan.Management/PingWSMan.cs index b9a0e21f1f4..88b443a6ef0 100644 --- a/src/Microsoft.WSMan.Management/PingWSMan.cs +++ b/src/Microsoft.WSMan.Management/PingWSMan.cs @@ -23,6 +23,7 @@ namespace Microsoft.WSMan.Management /// service is running. /// [Cmdlet(VerbsDiagnostic.Test, "WSMan", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2097114")] + [OutputType(typeof(XmlElement))] public class TestWSManCommand : AuthenticatingWSManCommand, IDisposable { /// diff --git a/src/Microsoft.WSMan.Management/Set-QuickConfig.cs b/src/Microsoft.WSMan.Management/Set-QuickConfig.cs index c318ea76477..1ee927558f4 100644 --- a/src/Microsoft.WSMan.Management/Set-QuickConfig.cs +++ b/src/Microsoft.WSMan.Management/Set-QuickConfig.cs @@ -30,6 +30,7 @@ namespace Microsoft.WSMan.Management /// 4. Enable firewall exception for WS-Management traffic. /// [Cmdlet(VerbsCommon.Set, "WSManQuickConfig", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097112")] + [OutputType(typeof(string))] public class SetWSManQuickConfigCommand : PSCmdlet, IDisposable { /// diff --git a/src/Microsoft.WSMan.Management/WSManConnections.cs b/src/Microsoft.WSMan.Management/WSManConnections.cs index c83ed7e9e10..cac5196740f 100644 --- a/src/Microsoft.WSMan.Management/WSManConnections.cs +++ b/src/Microsoft.WSMan.Management/WSManConnections.cs @@ -360,10 +360,7 @@ public string ComputerName protected override void BeginProcessing() { WSManHelper helper = new WSManHelper(this); - if (computername == null) - { - computername = "localhost"; - } + computername ??= "localhost"; if (this.SessionState.Path.CurrentProviderLocation(WSManStringLiterals.rootpath).Path.StartsWith(WSManStringLiterals.rootpath + ":" + WSManStringLiterals.DefaultPathSeparator + computername, StringComparison.OrdinalIgnoreCase)) { diff --git a/src/Microsoft.WSMan.Management/WSManInstance.cs b/src/Microsoft.WSMan.Management/WSManInstance.cs index a60ba270104..a29741172e2 100644 --- a/src/Microsoft.WSMan.Management/WSManInstance.cs +++ b/src/Microsoft.WSMan.Management/WSManInstance.cs @@ -29,6 +29,7 @@ namespace Microsoft.WSMan.Management /// -SelectorSet {Name=Spooler} /// [Cmdlet(VerbsCommon.Get, "WSManInstance", DefaultParameterSetName = "GetInstance", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096627")] + [OutputType(typeof(XmlElement))] public class GetWSManInstanceCommand : AuthenticatingWSManCommand, IDisposable { #region parameter @@ -685,6 +686,7 @@ protected override void EndProcessing() /// -SelectorSet {Name=Spooler} /// [Cmdlet(VerbsCommon.Set, "WSManInstance", DefaultParameterSetName = "ComputerName", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096937")] + [OutputType(typeof(XmlElement), typeof(string))] public class SetWSManInstanceCommand : AuthenticatingWSManCommand, IDisposable { #region Parameters @@ -1324,6 +1326,7 @@ protected override void ProcessRecord() /// using specified ValueSet or input File. /// [Cmdlet(VerbsCommon.New, "WSManInstance", DefaultParameterSetName = "ComputerName", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096933")] + [OutputType(typeof(XmlElement))] public class NewWSManInstanceCommand : AuthenticatingWSManCommand, IDisposable { /// diff --git a/src/Microsoft.WSMan.Management/WsManHelper.cs b/src/Microsoft.WSMan.Management/WsManHelper.cs index 000cb871abb..a4917ee461e 100644 --- a/src/Microsoft.WSMan.Management/WsManHelper.cs +++ b/src/Microsoft.WSMan.Management/WsManHelper.cs @@ -370,17 +370,8 @@ internal string ReadFile(string path) } finally { - if (_sr != null) - { - // _sr.Close(); - _sr.Dispose(); - } - - if (_fs != null) - { - // _fs.Close(); - _fs.Dispose(); - } + _sr?.Dispose(); + _fs?.Dispose(); } return strOut; diff --git a/src/Modules/PSGalleryModules.csproj b/src/Modules/PSGalleryModules.csproj index ca1c34df8b5..f2dfd6af95e 100644 --- a/src/Modules/PSGalleryModules.csproj +++ b/src/Modules/PSGalleryModules.csproj @@ -5,16 +5,16 @@ Microsoft Corporation (c) Microsoft Corporation. - net6.0 + net7.0 true - + - + diff --git a/src/Modules/Shared/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1 b/src/Modules/Shared/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1 index 0270ceffca0..e6d616aa61d 100644 --- a/src/Modules/Shared/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1 +++ b/src/Modules/Shared/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1 @@ -10,5 +10,5 @@ FunctionsToExport = @() CmdletsToExport="Start-Transcript", "Stop-Transcript" AliasesToExport = @() NestedModules="Microsoft.PowerShell.ConsoleHost.dll" -HelpInfoURI = 'https://aka.ms/powershell71-help' +HelpInfoURI = 'https://aka.ms/powershell73-help' } diff --git a/src/Modules/Unix/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 b/src/Modules/Unix/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 index da8f8707945..6eb576cdf03 100644 --- a/src/Modules/Unix/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 +++ b/src/Modules/Unix/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 @@ -7,7 +7,7 @@ ModuleVersion="7.0.0.0" CompatiblePSEditions = @("Core") PowerShellVersion="3.0" NestedModules="Microsoft.PowerShell.Commands.Management.dll" -HelpInfoURI = 'https://aka.ms/powershell71-help' +HelpInfoURI = 'https://aka.ms/powershell73-help' FunctionsToExport = @() AliasesToExport = @("gcb", "gtz", "scb") CmdletsToExport=@("Add-Content", diff --git a/src/Modules/Unix/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 b/src/Modules/Unix/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 index 1f4cc15e118..8268326aa74 100644 --- a/src/Modules/Unix/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 +++ b/src/Modules/Unix/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 @@ -1,14 +1,14 @@ @{ -GUID="A94C8C7E-9810-47C0-B8AF-65089C13A35A" -Author="PowerShell" -CompanyName="Microsoft Corporation" -Copyright="Copyright (c) Microsoft Corporation." -ModuleVersion="7.0.0.0" +GUID = "A94C8C7E-9810-47C0-B8AF-65089C13A35A" +Author = "PowerShell" +CompanyName = "Microsoft Corporation" +Copyright = "Copyright (c) Microsoft Corporation." +ModuleVersion = "7.0.0.0" CompatiblePSEditions = @("Core") -PowerShellVersion="3.0" +PowerShellVersion = "3.0" FunctionsToExport = @() -CmdletsToExport="Get-Credential", "Get-ExecutionPolicy", "Set-ExecutionPolicy", "ConvertFrom-SecureString", "ConvertTo-SecureString", "Get-PfxCertificate" , "Protect-CmsMessage", "Unprotect-CmsMessage", "Get-CmsMessage" +CmdletsToExport = "Get-Credential", "Get-ExecutionPolicy", "Set-ExecutionPolicy", "ConvertFrom-SecureString", "ConvertTo-SecureString", "Get-PfxCertificate" , "Protect-CmsMessage", "Unprotect-CmsMessage", "Get-CmsMessage" AliasesToExport = @() -NestedModules="Microsoft.PowerShell.Security.dll" -HelpInfoURI = 'https://aka.ms/powershell71-help' +NestedModules = "Microsoft.PowerShell.Security.dll" +HelpInfoURI = 'https://aka.ms/powershell73-help' } diff --git a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index b883acdd0d6..5761b3c745a 100644 --- a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -30,19 +30,5 @@ CmdletsToExport = @( FunctionsToExport = @() AliasesToExport = @('fhx') NestedModules = @("Microsoft.PowerShell.Commands.Utility.dll") -HelpInfoURI = 'https://aka.ms/powershell71-help' -PrivateData = @{ - PSData = @{ - ExperimentalFeatures = @( - @{ - Name = 'Microsoft.PowerShell.Utility.PSManageBreakpointsInRunspace' - Description = 'Enables -BreakAll parameter on Debug-Runspace and Debug-Job cmdlets to allow users to decide if they want PowerShell to break immediately in the current location when they attach a debugger. Enables -Runspace parameter on *-PSBreakpoint cmdlets to support management of breakpoints in another runspace.' - } - @{ - Name = 'Microsoft.PowerShell.Utility.PSImportPSDataFileSkipLimitCheck' - Description = 'Enable -SkipLimitCheck switch for Import-PowerShellDataFile to not enforce built-in hashtable limits' - } - ) - } -} +HelpInfoURI = 'https://aka.ms/powershell73-help' } diff --git a/src/Modules/Windows/CimCmdlets/CimCmdlets.psd1 b/src/Modules/Windows/CimCmdlets/CimCmdlets.psd1 index 4b38d4abc9e..93c68321ca1 100644 --- a/src/Modules/Windows/CimCmdlets/CimCmdlets.psd1 +++ b/src/Modules/Windows/CimCmdlets/CimCmdlets.psd1 @@ -14,5 +14,5 @@ CmdletsToExport= "Get-CimAssociatedInstance", "Get-CimClass", "Get-CimInstance", "Remove-CimSession","Set-CimInstance", "Export-BinaryMiLog","Import-BinaryMiLog" AliasesToExport = "gcim","scim","ncim", "rcim","icim","gcai","rcie","ncms","rcms","gcms","ncso","gcls" -HelpInfoUri="https://aka.ms/powershell71-help" +HelpInfoUri="https://aka.ms/powershell73-help" } diff --git a/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1 b/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1 index ed2344b51b2..50282d8d5b8 100644 --- a/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1 +++ b/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1 @@ -12,5 +12,5 @@ AliasesToExport = @() NestedModules="Microsoft.PowerShell.Commands.Diagnostics.dll" TypesToProcess="GetEvent.types.ps1xml" FormatsToProcess="Event.format.ps1xml", "Diagnostics.format.ps1xml" -HelpInfoURI = 'https://aka.ms/powershell71-help' +HelpInfoURI = 'https://aka.ms/powershell73-help' } diff --git a/src/Modules/Windows/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 b/src/Modules/Windows/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 index f7cd1dc6ace..0b49f178b25 100644 --- a/src/Modules/Windows/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 +++ b/src/Modules/Windows/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 @@ -7,7 +7,7 @@ ModuleVersion="7.0.0.0" CompatiblePSEditions = @("Core") PowerShellVersion="3.0" NestedModules="Microsoft.PowerShell.Commands.Management.dll" -HelpInfoURI = 'https://aka.ms/powershell71-help' +HelpInfoURI = 'https://aka.ms/powershell73-help' FunctionsToExport = @() AliasesToExport = @("gcb", "gin", "gtz", "scb", "stz") CmdletsToExport=@("Add-Content", diff --git a/src/Modules/Windows/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 b/src/Modules/Windows/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 index cbc5b2dc78e..7470c795fdc 100644 --- a/src/Modules/Windows/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 +++ b/src/Modules/Windows/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 @@ -1,14 +1,18 @@ @{ -GUID="A94C8C7E-9810-47C0-B8AF-65089C13A35A" -Author="PowerShell" -CompanyName="Microsoft Corporation" -Copyright="Copyright (c) Microsoft Corporation." -ModuleVersion="7.0.0.0" +GUID = "A94C8C7E-9810-47C0-B8AF-65089C13A35A" +Author = "PowerShell" +CompanyName = "Microsoft Corporation" +Copyright = "Copyright (c) Microsoft Corporation." +ModuleVersion = "7.0.0.0" CompatiblePSEditions = @("Core") -PowerShellVersion="3.0" +PowerShellVersion = "3.0" FunctionsToExport = @() -CmdletsToExport="Get-Acl", "Set-Acl", "Get-PfxCertificate", "Get-Credential", "Get-ExecutionPolicy", "Set-ExecutionPolicy", "Get-AuthenticodeSignature", "Set-AuthenticodeSignature", "ConvertFrom-SecureString", "ConvertTo-SecureString", "Get-CmsMessage", "Unprotect-CmsMessage", "Protect-CmsMessage" , "New-FileCatalog" , "Test-FileCatalog" +CmdletsToExport = "Get-Acl", "Set-Acl", "Get-PfxCertificate", "Get-Credential", "Get-ExecutionPolicy", "Set-ExecutionPolicy", "Get-AuthenticodeSignature", "Set-AuthenticodeSignature", "ConvertFrom-SecureString", "ConvertTo-SecureString", "Get-CmsMessage", "Unprotect-CmsMessage", "Protect-CmsMessage" , "New-FileCatalog" , "Test-FileCatalog" AliasesToExport = @() -NestedModules="Microsoft.PowerShell.Security.dll" -HelpInfoURI = 'https://aka.ms/powershell71-help' +NestedModules = "Microsoft.PowerShell.Security.dll" +# 'Security.types.ps1xml' refers to types from 'Microsoft.PowerShell.Security.dll' and thus requiring to load the assembly before processing the type file. +# We declare 'Microsoft.PowerShell.Security.dll' in 'RequiredAssemblies' so as to make sure it's loaded before the type file processing. +RequiredAssemblies = "Microsoft.PowerShell.Security.dll" +TypesToProcess = "Security.types.ps1xml" +HelpInfoURI = 'https://aka.ms/powershell73-help' } diff --git a/src/Modules/Windows/Microsoft.PowerShell.Security/Security.types.ps1xml b/src/Modules/Windows/Microsoft.PowerShell.Security/Security.types.ps1xml new file mode 100644 index 00000000000..b1171c98e6a --- /dev/null +++ b/src/Modules/Windows/Microsoft.PowerShell.Security/Security.types.ps1xml @@ -0,0 +1,124 @@ + + + + + + System.Security.AccessControl.ObjectSecurity + + + Path + + Microsoft.PowerShell.Commands.SecurityDescriptorCommandsBase + GetPath + + + + Owner + + Microsoft.PowerShell.Commands.SecurityDescriptorCommandsBase + GetOwner + + + + Group + + Microsoft.PowerShell.Commands.SecurityDescriptorCommandsBase + GetGroup + + + + Access + + Microsoft.PowerShell.Commands.SecurityDescriptorCommandsBase + GetAccess + + + + Sddl + + Microsoft.PowerShell.Commands.SecurityDescriptorCommandsBase + GetSddl + + + + AccessToString + + $toString = ""; + $first = $true; + if ( ! $this.Access ) { return "" } + foreach($ace in $this.Access) + { + if($first) + { + $first = $false; + } + else + { + $tostring += "`n"; + } + $toString += $ace.IdentityReference.ToString(); + $toString += " "; + $toString += $ace.AccessControlType.ToString(); + $toString += " "; + if($ace -is [System.Security.AccessControl.FileSystemAccessRule]) + { + $toString += $ace.FileSystemRights.ToString(); + } + elseif($ace -is [System.Security.AccessControl.RegistryAccessRule]) + { + $toString += $ace.RegistryRights.ToString(); + } + } + return $toString; + + + + AuditToString + + $toString = ""; + $first = $true; + if ( ! (& { Set-StrictMode -Version 1; $this.audit }) ) { return "" } + foreach($ace in (& { Set-StrictMode -Version 1; $this.audit })) + { + if($first) + { + $first = $false; + } + else + { + $tostring += "`n"; + } + $toString += $ace.IdentityReference.ToString(); + $toString += " "; + $toString += $ace.AuditFlags.ToString(); + $toString += " "; + if($ace -is [System.Security.AccessControl.FileSystemAuditRule]) + { + $toString += $ace.FileSystemRights.ToString(); + } + elseif($ace -is [System.Security.AccessControl.RegistryAuditRule]) + { + $toString += $ace.RegistryRights.ToString(); + } + } + return $toString; + + + + + + diff --git a/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index 5ea504197fa..7d0e7a91e11 100644 --- a/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -29,19 +29,5 @@ CmdletsToExport = @( FunctionsToExport = @() AliasesToExport = @('fhx') NestedModules = @("Microsoft.PowerShell.Commands.Utility.dll") -HelpInfoURI = 'https://aka.ms/powershell71-help' -PrivateData = @{ - PSData = @{ - ExperimentalFeatures = @( - @{ - Name = 'Microsoft.PowerShell.Utility.PSManageBreakpointsInRunspace' - Description = 'Enables -BreakAll parameter on Debug-Runspace and Debug-Job cmdlets to allow users to decide if they want PowerShell to break immediately in the current location when they attach a debugger. Enables -Runspace parameter on *-PSBreakpoint cmdlets to support management of breakpoints in another runspace.' - } - @{ - Name = 'Microsoft.PowerShell.Utility.PSImportPSDataFileSkipLimitCheck' - Description = 'Enable -NoLimit switch for Import-PowerShellDataFile to not enforce built-in hashtable limits' - } - ) - } -} +HelpInfoURI = 'https://aka.ms/powershell73-help' } diff --git a/src/Modules/Windows/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1 b/src/Modules/Windows/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1 index d2bb2398541..5eb367b7e7f 100644 --- a/src/Modules/Windows/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1 +++ b/src/Modules/Windows/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1 @@ -11,5 +11,5 @@ CmdletsToExport="Disable-WSManCredSSP", "Enable-WSManCredSSP", "Get-WSManCredSSP AliasesToExport = @() NestedModules="Microsoft.WSMan.Management.dll" FormatsToProcess="WSMan.format.ps1xml" -HelpInfoURI = 'https://aka.ms/powershell71-help' +HelpInfoURI = 'https://aka.ms/powershell73-help' } diff --git a/src/Modules/Windows/PSDiagnostics/PSDiagnostics.psd1 b/src/Modules/Windows/PSDiagnostics/PSDiagnostics.psd1 index dded04d4920..6185b589a82 100644 --- a/src/Modules/Windows/PSDiagnostics/PSDiagnostics.psd1 +++ b/src/Modules/Windows/PSDiagnostics/PSDiagnostics.psd1 @@ -10,5 +10,5 @@ FunctionsToExport="Disable-PSTrace","Disable-PSWSManCombinedTrace","Disable-WSManTrace","Enable-PSTrace","Enable-PSWSManCombinedTrace","Enable-WSManTrace","Get-LogProperties","Set-LogProperties","Start-Trace","Stop-Trace" CmdletsToExport = @() AliasesToExport = @() - HelpInfoUri="https://aka.ms/powershell71-help" + HelpInfoUri="https://aka.ms/powershell73-help" } diff --git a/src/ResGen/ResGen.csproj b/src/ResGen/ResGen.csproj index 5c736ad9ed9..f86d5328ba8 100644 --- a/src/ResGen/ResGen.csproj +++ b/src/ResGen/ResGen.csproj @@ -2,7 +2,7 @@ Generates C# typed bindings for .resx files - net6.0 + net7.0 resgen Exe true diff --git a/src/Schemas/PSMaml/Maml_HTML_Style.xsl b/src/Schemas/PSMaml/Maml_HTML_Style.xsl index 3f66ee3f968..955966526cc 100644 --- a/src/Schemas/PSMaml/Maml_HTML_Style.xsl +++ b/src/Schemas/PSMaml/Maml_HTML_Style.xsl @@ -172,7 +172,7 @@ + this cell is the table header, footer or body --> bottom @@ -381,4 +381,4 @@ - \ No newline at end of file + diff --git a/src/System.Management.Automation/CoreCLR/CorePsAssemblyLoadContext.cs b/src/System.Management.Automation/CoreCLR/CorePsAssemblyLoadContext.cs index 43e6ad17eb4..0e547138220 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsAssemblyLoadContext.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsAssemblyLoadContext.cs @@ -232,6 +232,9 @@ internal IEnumerable GetAssembly(string namespaceQualifiedTypeName) /// | /// |--- 'osx-x64' subfolder /// | |--- native.dylib + /// | + /// |--- 'osx-arm64' subfolder + /// | |--- native.dylib /// internal static IntPtr NativeDllHandler(Assembly assembly, string libraryName) { @@ -343,9 +346,6 @@ private bool TryFindInGAC(AssemblyName assemblyName, out string assemblyFilePath return false; } - bool assemblyFound = false; - char dirSeparator = IO.Path.DirectorySeparatorChar; - if (string.IsNullOrEmpty(_winDir)) { // cache value of '_winDir' folder in member variable. @@ -355,21 +355,21 @@ private bool TryFindInGAC(AssemblyName assemblyName, out string assemblyFilePath if (string.IsNullOrEmpty(_gacPathMSIL)) { // cache value of '_gacPathMSIL' folder in member variable. - _gacPathMSIL = $"{_winDir}{dirSeparator}Microsoft.NET{dirSeparator}assembly{dirSeparator}GAC_MSIL"; + _gacPathMSIL = Path.Join(_winDir, "Microsoft.NET", "assembly", "GAC_MSIL"); } - assemblyFound = FindInGac(_gacPathMSIL, assemblyName, out assemblyFilePath); + bool assemblyFound = FindInGac(_gacPathMSIL, assemblyName, out assemblyFilePath); if (!assemblyFound) { - string gacBitnessAwarePath = null; + string gacBitnessAwarePath; if (Environment.Is64BitProcess) { if (string.IsNullOrEmpty(_gacPath64)) { - // cache value of '_gacPath64' folder in member variable. - _gacPath64 = $"{_winDir}{dirSeparator}Microsoft.NET{dirSeparator}assembly{dirSeparator}GAC_64"; + var gacName = RuntimeInformation.ProcessArchitecture == Architecture.Arm64 ? "GAC_Arm64" : "GAC_64"; + _gacPath64 = Path.Join(_winDir, "Microsoft.NET", "assembly", gacName); } gacBitnessAwarePath = _gacPath64; @@ -378,8 +378,7 @@ private bool TryFindInGAC(AssemblyName assemblyName, out string assemblyFilePath { if (string.IsNullOrEmpty(_gacPath32)) { - // cache value of '_gacPath32' folder in member variable. - _gacPath32 = $"{_winDir}{dirSeparator}Microsoft.NET{dirSeparator}assembly{dirSeparator}GAC_32"; + _gacPath32 = Path.Join(_winDir, "Microsoft.NET", "assembly", "GAC_32"); } gacBitnessAwarePath = _gacPath32; @@ -397,8 +396,7 @@ private static bool FindInGac(string gacRoot, AssemblyName assemblyName, out str bool assemblyFound = false; assemblyPath = null; - char dirSeparator = IO.Path.DirectorySeparatorChar; - string tempAssemblyDirPath = $"{gacRoot}{dirSeparator}{assemblyName.Name}"; + string tempAssemblyDirPath = Path.Join(gacRoot, assemblyName.Name); if (Directory.Exists(tempAssemblyDirPath)) { @@ -540,19 +538,19 @@ private static string GetNativeDllSubFolderName(out string ext) ext = string.Empty; var processArch = RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant(); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (Platform.IsWindows) { folderName = "win-" + processArch; ext = ".dll"; } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + else if (Platform.IsLinux) { folderName = "linux-" + processArch; ext = ".so"; } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + else if (Platform.IsMacOS) { - folderName = "osx-x64"; + folderName = "osx-" + processArch; ext = ".dylib"; } diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 884e91e3356..1986c4c63be 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -5,9 +5,8 @@ using System.ComponentModel; using System.IO; using System.Runtime.InteropServices; - +using System.Management.Automation.Internal; using Microsoft.Win32; -using Microsoft.Win32.SafeHandles; namespace System.Management.Automation { @@ -23,7 +22,7 @@ public static bool IsLinux { get { - return RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + return OperatingSystem.IsLinux(); } } @@ -34,7 +33,7 @@ public static bool IsMacOS { get { - return RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + return OperatingSystem.IsMacOS(); } } @@ -45,7 +44,7 @@ public static bool IsWindows { get { - return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + return OperatingSystem.IsWindows(); } } @@ -161,6 +160,7 @@ public static bool IsStaSupported // Gets the location for cache and config folders. internal static readonly string CacheDirectory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Microsoft\PowerShell"; internal static readonly string ConfigDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\PowerShell"; + private static readonly Lazy _isStaSupported = new Lazy(() => { // See objbase.h @@ -184,7 +184,7 @@ public static bool IsStaSupported #endif // format files - internal static readonly List FormatFileNames = new() + internal static readonly string[] FormatFileNames = new string[] { "Certificate.format.ps1xml", "Diagnostics.format.ps1xml", @@ -199,8 +199,6 @@ public static bool IsStaSupported "WSMan.format.ps1xml" }; - private static string _tempDirectory = null; - /// /// Some common environment variables used in PS have different /// names in different OS platforms. @@ -214,43 +212,37 @@ internal static class CommonEnvVariableNames #endif } +#if UNIX + private static string s_tempHome = null; + /// - /// Remove the temporary directory created for the current process. + /// Get the 'HOME' environment variable or create a temporary home diretory if the environment variable is not set. /// - internal static void RemoveTemporaryDirectory() + private static string GetHomeOrCreateTempHome() { - if (_tempDirectory == null) + const string tempHomeFolderName = "pwsh-{0}-98288ff9-5712-4a14-9a11-23693b9cd91a"; + + string envHome = Environment.GetEnvironmentVariable("HOME") ?? s_tempHome; + if (envHome is not null) { - return; + return envHome; } try { - Directory.Delete(_tempDirectory, true); + s_tempHome = Path.Combine(Path.GetTempPath(), StringUtil.Format(tempHomeFolderName, Environment.UserName)); + Directory.CreateDirectory(s_tempHome); } - catch - { - // ignore if there is a failure - } - - _tempDirectory = null; - } - - /// - /// Get a temporary directory to use for the current process. - /// - internal static string GetTemporaryDirectory() - { - if (_tempDirectory != null) + catch (UnauthorizedAccessException) { - return _tempDirectory; + // Directory creation may fail if the account doesn't have filesystem permission such as some service accounts. + // Return an empty string in this case so the process working directory will be used. + s_tempHome = string.Empty; } - _tempDirectory = PsUtils.GetTemporaryDirectory(); - return _tempDirectory; + return s_tempHome; } -#if UNIX /// /// X Desktop Group configuration type enum. /// @@ -270,230 +262,100 @@ public enum XDG_Type DEFAULT } - private static string s_tempHomeDir = null; - /// /// Function for choosing directory location of PowerShell for profile loading. /// - public static string SelectProductNameForDirectory(Platform.XDG_Type dirpath) + public static string SelectProductNameForDirectory(XDG_Type dirpath) { // TODO: XDG_DATA_DIRS implementation as per GitHub issue #1060 - string xdgconfighome = System.Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); - string xdgdatahome = System.Environment.GetEnvironmentVariable("XDG_DATA_HOME"); - string xdgcachehome = System.Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); - string envHome = System.Environment.GetEnvironmentVariable(CommonEnvVariableNames.Home); - if (envHome == null) - { - s_tempHomeDir ??= GetTemporaryDirectory(); - envHome = s_tempHomeDir; - } + string xdgconfighome = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); + string xdgdatahome = Environment.GetEnvironmentVariable("XDG_DATA_HOME"); + string xdgcachehome = Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); + string envHome = GetHomeOrCreateTempHome(); string xdgConfigHomeDefault = Path.Combine(envHome, ".config", "powershell"); string xdgDataHomeDefault = Path.Combine(envHome, ".local", "share", "powershell"); string xdgModuleDefault = Path.Combine(xdgDataHomeDefault, "Modules"); string xdgCacheDefault = Path.Combine(envHome, ".cache", "powershell"); - switch (dirpath) + try { - case Platform.XDG_Type.CONFIG: - // the user has set XDG_CONFIG_HOME corresponding to profile path - if (string.IsNullOrEmpty(xdgconfighome)) - { - // xdg values have not been set - return xdgConfigHomeDefault; - } - - else - { - return Path.Combine(xdgconfighome, "powershell"); - } - - case Platform.XDG_Type.DATA: - // the user has set XDG_DATA_HOME corresponding to module path - if (string.IsNullOrEmpty(xdgdatahome)) - { - // create the xdg folder if needed - if (!Directory.Exists(xdgDataHomeDefault)) + switch (dirpath) + { + case XDG_Type.CONFIG: + // Use 'XDG_CONFIG_HOME' if it's set, otherwise use the default path. + return string.IsNullOrEmpty(xdgconfighome) + ? xdgConfigHomeDefault + : Path.Combine(xdgconfighome, "powershell"); + + case XDG_Type.DATA: + // Use 'XDG_DATA_HOME' if it's set, otherwise use the default path. + if (string.IsNullOrEmpty(xdgdatahome)) { - try - { - Directory.CreateDirectory(xdgDataHomeDefault); - } - catch (UnauthorizedAccessException) - { - // service accounts won't have permission to create user folder - return GetTemporaryDirectory(); - } + // Create the default data directory if it doesn't exist. + Directory.CreateDirectory(xdgDataHomeDefault); + return xdgDataHomeDefault; } - - return xdgDataHomeDefault; - } - else - { return Path.Combine(xdgdatahome, "powershell"); - } - case Platform.XDG_Type.USER_MODULES: - // the user has set XDG_DATA_HOME corresponding to module path - if (string.IsNullOrEmpty(xdgdatahome)) - { - // xdg values have not been set - if (!Directory.Exists(xdgModuleDefault)) // module folder not always guaranteed to exist + case XDG_Type.USER_MODULES: + // Use 'XDG_DATA_HOME' if it's set, otherwise use the default path. + if (string.IsNullOrEmpty(xdgdatahome)) { - try - { - Directory.CreateDirectory(xdgModuleDefault); - } - catch (UnauthorizedAccessException) - { - // service accounts won't have permission to create user folder - return GetTemporaryDirectory(); - } + Directory.CreateDirectory(xdgModuleDefault); + return xdgModuleDefault; } - - return xdgModuleDefault; - } - else - { return Path.Combine(xdgdatahome, "powershell", "Modules"); - } - case Platform.XDG_Type.SHARED_MODULES: - return "/usr/local/share/powershell/Modules"; + case XDG_Type.SHARED_MODULES: + return "/usr/local/share/powershell/Modules"; - case Platform.XDG_Type.CACHE: - // the user has set XDG_CACHE_HOME - if (string.IsNullOrEmpty(xdgcachehome)) - { - // xdg values have not been set - if (!Directory.Exists(xdgCacheDefault)) // module folder not always guaranteed to exist + case XDG_Type.CACHE: + // Use 'XDG_CACHE_HOME' if it's set, otherwise use the default path. + if (string.IsNullOrEmpty(xdgcachehome)) { - try - { - Directory.CreateDirectory(xdgCacheDefault); - } - catch (UnauthorizedAccessException) - { - // service accounts won't have permission to create user folder - return GetTemporaryDirectory(); - } + Directory.CreateDirectory(xdgCacheDefault); + return xdgCacheDefault; } - return xdgCacheDefault; - } - else - { - if (!Directory.Exists(Path.Combine(xdgcachehome, "powershell"))) - { - try - { - Directory.CreateDirectory(Path.Combine(xdgcachehome, "powershell")); - } - catch (UnauthorizedAccessException) - { - // service accounts won't have permission to create user folder - return GetTemporaryDirectory(); - } - } + string cachePath = Path.Combine(xdgcachehome, "powershell"); + Directory.CreateDirectory(cachePath); + return cachePath; - return Path.Combine(xdgcachehome, "powershell"); - } - - case Platform.XDG_Type.DEFAULT: - // default for profile location - return xdgConfigHomeDefault; - - default: - // xdgConfigHomeDefault needs to be created in the edge case that we do not have the folder or it was deleted - // This folder is the default in the event of all other failures for data storage - if (!Directory.Exists(xdgConfigHomeDefault)) - { - try - { - Directory.CreateDirectory(xdgConfigHomeDefault); - } - catch - { - Console.Error.WriteLine("Failed to create default data directory: " + xdgConfigHomeDefault); - } - } + case XDG_Type.DEFAULT: + // Use 'xdgConfigHomeDefault' for 'XDG_Type.DEFAULT' and create the directory if it doesn't exist. + Directory.CreateDirectory(xdgConfigHomeDefault); + return xdgConfigHomeDefault; - return xdgConfigHomeDefault; + default: + throw new InvalidOperationException("Unreachable code."); + } + } + catch (UnauthorizedAccessException) + { + // Directory creation may fail if the account doesn't have filesystem permission such as some service accounts. + // Return an empty string in this case so the process working directory will be used. + return string.Empty; } } #endif /// - /// The code is copied from the .NET implementation. - /// - internal static string GetFolderPath(System.Environment.SpecialFolder folder) - { - return InternalGetFolderPath(folder); - } - - /// - /// The API set 'api-ms-win-shell-shellfolders-l1-1-0.dll' was removed from NanoServer, so we cannot depend on 'SHGetFolderPathW' - /// to get the special folder paths. Instead, we need to rely on the basic environment variables to get the special folder paths. + /// Mimic 'Environment.GetFolderPath(folder)' on Unix. /// - /// - /// The path to the specified system special folder, if that folder physically exists on your computer. - /// Otherwise, an empty string (string.Empty). - /// - private static string InternalGetFolderPath(System.Environment.SpecialFolder folder) + internal static string GetFolderPath(Environment.SpecialFolder folder) { - string folderPath = null; #if UNIX - string envHome = System.Environment.GetEnvironmentVariable(Platform.CommonEnvVariableNames.Home); - if (envHome == null) + return folder switch { - envHome = Platform.GetTemporaryDirectory(); - } - - switch (folder) - { - case System.Environment.SpecialFolder.ProgramFiles: - folderPath = "/bin"; - if (!System.IO.Directory.Exists(folderPath)) { folderPath = null; } - - break; - case System.Environment.SpecialFolder.ProgramFilesX86: - folderPath = "/usr/bin"; - if (!System.IO.Directory.Exists(folderPath)) { folderPath = null; } - - break; - case System.Environment.SpecialFolder.System: - case System.Environment.SpecialFolder.SystemX86: - folderPath = "/sbin"; - if (!System.IO.Directory.Exists(folderPath)) { folderPath = null; } - - break; - case System.Environment.SpecialFolder.Personal: - folderPath = envHome; - break; - case System.Environment.SpecialFolder.LocalApplicationData: - folderPath = System.IO.Path.Combine(envHome, ".config"); - if (!System.IO.Directory.Exists(folderPath)) - { - try - { - System.IO.Directory.CreateDirectory(folderPath); - } - catch (UnauthorizedAccessException) - { - // directory creation may fail if the account doesn't have filesystem permission such as some service accounts - folderPath = string.Empty; - } - } - - break; - default: - throw new NotSupportedException(); - } + Environment.SpecialFolder.ProgramFiles => Directory.Exists("/bin") ? "/bin" : string.Empty, + Environment.SpecialFolder.MyDocuments => GetHomeOrCreateTempHome(), + _ => throw new NotSupportedException() + }; #else - folderPath = System.Environment.GetFolderPath(folder); + return Environment.GetFolderPath(folder); #endif - return folderPath ?? string.Empty; } // Platform methods prefixed NonWindows are: @@ -516,11 +378,6 @@ internal static bool NonWindowsIsHardLink(FileSystemInfo fileInfo) return Unix.IsHardLink(fileInfo); } - internal static string NonWindowsInternalGetTarget(string path) - { - return Unix.NativeMethods.FollowSymLink(path); - } - internal static string NonWindowsGetUserFromPid(int path) { return Unix.NativeMethods.GetUserFromPid(path); @@ -563,11 +420,9 @@ internal static bool NonWindowsIsSameFileSystemItem(string pathOne, string pathT return Unix.NativeMethods.IsSameFileSystemItem(pathOne, pathTwo); } - internal static bool NonWindowsGetInodeData(string path, out System.ValueTuple inodeData) + internal static bool NonWindowsGetInodeData(string path, out ValueTuple inodeData) { - UInt64 device = 0UL; - UInt64 inode = 0UL; - var result = Unix.NativeMethods.GetInodeData(path, out device, out inode); + var result = Unix.NativeMethods.GetInodeData(path, out ulong device, out ulong inode); inodeData = (device, inode); return result == 0; @@ -1041,7 +896,7 @@ public static int GetProcFSParentPid(int pid) return invalidPid; } - return Int32.Parse(parts[3]); + return int.Parse(parts[3]); } catch (Exception) { @@ -1139,10 +994,6 @@ internal static extern int CreateSymLink([MarshalAs(UnmanagedType.LPStr)] string internal static extern int CreateHardLink([MarshalAs(UnmanagedType.LPStr)] string filePath, [MarshalAs(UnmanagedType.LPStr)] string target); - [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] - [return: MarshalAs(UnmanagedType.LPStr)] - internal static extern string FollowSymLink([MarshalAs(UnmanagedType.LPStr)] string filePath); - [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] [return: MarshalAs(UnmanagedType.LPStr)] internal static extern string GetUserFromPid(int pid); @@ -1154,7 +1005,7 @@ internal static extern bool IsSameFileSystemItem([MarshalAs(UnmanagedType.LPStr) [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] internal static extern int GetInodeData([MarshalAs(UnmanagedType.LPStr)] string path, - out UInt64 device, out UInt64 inode); + out ulong device, out ulong inode); /// /// This is a struct from getcommonstat.h in the native library. diff --git a/src/System.Management.Automation/CoreCLR/CorePsStub.cs b/src/System.Management.Automation/CoreCLR/CorePsStub.cs index d99bcbb398b..67775cde747 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsStub.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsStub.cs @@ -457,6 +457,20 @@ internal static bool IsClassInApprovedList(Guid clsid) { throw new NotImplementedException("SystemPolicy.IsClassInApprovedList not implemented"); } + + /// + /// Gets the system wide script file policy enforcement for an open file. + /// Based on system WDAC (Windows Defender Application Control) or AppLocker policies. + /// + /// Script file path for policy check. + /// FileStream object to script file path. + /// Policy check result for script file. + public static SystemScriptFileEnforcement GetFilePolicyEnforcement( + string filePath, + System.IO.FileStream fileStream) + { + return SystemScriptFileEnforcement.None; + } } /// @@ -473,6 +487,32 @@ internal enum SystemEnforcementMode /// Enabled, enforce restrictions Enforce = 2 } + + /// + /// System wide policy enforcement for a specific script file. + /// + public enum SystemScriptFileEnforcement + { + /// + /// No policy enforcement. + /// + None = 0, + + /// + /// Script file is blocked from running. + /// + Block = 1, + + /// + /// Script file is allowed to run without restrictions (FullLanguage mode). + /// + Allow = 2, + + /// + /// Script file is allowed to run in ConstrainedLanguage mode only. + /// + AllowConstrained = 3 + } } // Porting note: Tracing is absolutely not available on Linux diff --git a/src/System.Management.Automation/DscSupport/CimDSCParser.cs b/src/System.Management.Automation/DscSupport/CimDSCParser.cs index f518d18fa1c..ccefa7e24bb 100644 --- a/src/System.Management.Automation/DscSupport/CimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/CimDSCParser.cs @@ -561,10 +561,7 @@ private static Dictionary ClassCache { get { - if (t_classCache == null) - { - t_classCache = new Dictionary(StringComparer.OrdinalIgnoreCase); - } + t_classCache ??= new Dictionary(StringComparer.OrdinalIgnoreCase); return t_classCache; } @@ -580,10 +577,7 @@ private static Dictionary> ByClassModuleCache { get { - if (t_byClassModuleCache == null) - { - t_byClassModuleCache = new Dictionary>(StringComparer.OrdinalIgnoreCase); - } + t_byClassModuleCache ??= new Dictionary>(StringComparer.OrdinalIgnoreCase); return t_byClassModuleCache; } @@ -599,10 +593,7 @@ private static Dictionary> ByClassModuleCache { get { - if (t_byFileClassCache == null) - { - t_byFileClassCache = new Dictionary>(StringComparer.OrdinalIgnoreCase); - } + t_byFileClassCache ??= new Dictionary>(StringComparer.OrdinalIgnoreCase); return t_byFileClassCache; } @@ -618,10 +609,7 @@ private static HashSet ScriptKeywordFileCache { get { - if (t_scriptKeywordFileCache == null) - { - t_scriptKeywordFileCache = new HashSet(StringComparer.OrdinalIgnoreCase); - } + t_scriptKeywordFileCache ??= new HashSet(StringComparer.OrdinalIgnoreCase); return t_scriptKeywordFileCache; } @@ -738,12 +726,12 @@ public static void Initialize(Collection errors, List moduleP if (!Directory.Exists(systemResourceRoot)) { - configSystemPath = Platform.GetFolderPath(Environment.SpecialFolder.System); + configSystemPath = Environment.GetFolderPath(Environment.SpecialFolder.System); systemResourceRoot = Path.Combine(configSystemPath, "Configuration"); inboxModulePath = InboxDscResourceModulePath; } - var programFilesDirectory = Platform.GetFolderPath(Environment.SpecialFolder.ProgramFiles); + var programFilesDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); Debug.Assert(programFilesDirectory != null, "Program Files environment variable does not exist!"); var customResourceRoot = Path.Combine(programFilesDirectory, "WindowsPowerShell\\Configuration"); Debug.Assert(Directory.Exists(customResourceRoot), "%ProgramFiles%\\WindowsPowerShell\\Configuration Directory does not exist"); @@ -982,10 +970,7 @@ public static List ImportClasses(string path, Tuple m { // Ignore modules with invalid schemas. s_tracer.WriteLine("DSC ClassCache: Error importing file '{0}', with error '{1}'. Skipping file.", path, e); - if (errors != null) - { - errors.Add(e); - } + errors?.Add(e); } if (classes != null) @@ -1012,10 +997,7 @@ public static List ImportClasses(string path, Tuple m ParserStrings.DuplicateCimClassDefinition, className, path, files); e.SetErrorId("DuplicateCimClassDefinition"); - if (errors != null) - { - errors.Add(e); - } + errors?.Add(e); } } @@ -1893,10 +1875,7 @@ private static ParseError[] ImportResourceCheckSemantics(DynamicKeywordStatement { if (keywordAst.Keyword.Keyword.Equals("Node")) { - if (errorList == null) - { - errorList = new List(); - } + errorList ??= new List(); errorList.Add(new ParseError(kwAst.Extent, "ImportDscResourceInsideNode", @@ -2148,8 +2127,7 @@ public static void LoadResourcesFromModule(IScriptExtent scriptExtent, { try { - string unused; - foundResources = ImportCimKeywordsFromModule(moduleInfo, resourceToImport, out unused); + foundResources = ImportCimKeywordsFromModule(moduleInfo, resourceToImport, out _); } catch (Exception) { diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 4e359c3acf9..9b2f6596bd5 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -811,33 +811,18 @@ private static IEnumerable ViewsOf_System_Management_Autom $ellipsis = ""`u{2026}"" $resetColor = '' $errorColor = '' - if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) { - if ($null -ne $psstyle) { - $errorColor = $psstyle.Formatting.Error - } - - $resetColor = [System.Management.Automation.VTUtility]::GetEscapeSequence( - [System.Management.Automation.VTUtility+VT]::Reset - ) - } + $accentColor = '' - function Get-VT100Color([ConsoleColor] $color) { - if (!$Host.UI.SupportsVirtualTerminal -or !([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) { - return '' - } - - return [System.Management.Automation.VTUtility]::GetEscapeSequence($color) + if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) { + $resetColor = $PSStyle.Reset + $errorColor = $psstyle.Formatting.Error + $accentColor = $PSStyle.Formatting.FormatAccent } function Show-ErrorRecord($obj, [int]$indent = 0, [int]$depth = 1) { $newline = [Environment]::Newline $output = [System.Text.StringBuilder]::new() $prefix = ' ' * $indent - $accentColor = '' - - if ($null -ne $Host.PrivateData) { - $accentColor = Get-VT100Color ($Host.PrivateData.FormatAccentColor ?? $Host.PrivateData.ErrorForegroundColor) - } $expandTypes = @( 'Microsoft.Rest.HttpRequestMessageWrapper' @@ -1066,36 +1051,17 @@ private static IEnumerable ViewsOf_System_Management_Autom Set-StrictMode -Off $newline = [Environment]::Newline - function Get-ConciseViewPositionMessage { - - $resetColor = '' - if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) { - $resetColor = [System.Management.Automation.VTUtility]::GetEscapeSequence( - [System.Management.Automation.VTUtility+VT]::Reset - ) - } - - function Get-VT100Color([ConsoleColor] $color) { - if (!$Host.UI.SupportsVirtualTerminal -or !([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) { - return '' - } - - return [System.Management.Automation.VTUtility]::GetEscapeSequence($color) - } + $resetColor = '' + $errorColor = '' + $accentColor = '' - # return length of string sans VT100 codes - function Get-RawStringLength($string) { - $vtCodes = ""`e[0m"", ""`e[2;30m"", ""`e[2;31m"", ""`e[2;32m"", ""`e[2;33m"", ""`e[2;34m"", - ""`e[2;35m"", ""`e[2;36m"", ""`e[2;37m"", ""`e[1;30m"", ""`e[1;31m"", ""`e[1;32m"", - ""`e[1;33m"", ""`e[1;34m"", ""`e[1;35m"", ""`e[1;36m"", ""`e[1;37m"" - - $newString = $string - foreach ($vtCode in $vtCodes) { - $newString = $newString.Replace($vtCode, '') - } + if ($Host.UI.SupportsVirtualTerminal -and ([string]::IsNullOrEmpty($env:__SuppressAnsiEscapeSequences))) { + $resetColor = $PSStyle.Reset + $errorColor = $PSStyle.Formatting.Error + $accentColor = $PSStyle.Formatting.ErrorAccent + } - return $newString.Length - } + function Get-ConciseViewPositionMessage { # returns a string cut to last whitespace function Get-TruncatedString($string, [int]$length) { @@ -1107,14 +1073,6 @@ function Get-ConciseViewPositionMessage { return ($string.Substring(0,$length) -split '\s',-2)[0] } - $errorColor = '' - $accentColor = '' - - if ($null -ne $Host.PrivateData) { - $errorColor = Get-VT100Color $Host.PrivateData.ErrorForegroundColor - $accentColor = Get-VT100Color ($Host.PrivateData.ErrorAccentColor ?? $errorColor) - } - $posmsg = '' $headerWhitespace = '' $offsetWhitespace = '' @@ -1217,7 +1175,7 @@ function Get-ConciseViewPositionMessage { # if rendering line information, break up the message if it's wider than the console if ($myinv -and $myinv.ScriptName -or $err.CategoryInfo.Category -eq 'ParserError') { - $prefixLength = Get-RawStringLength -string $prefix + $prefixLength = [System.Management.Automation.Internal.StringDecorated]::new($prefix).ContentLength $prefixVtLength = $prefix.Length - $prefixLength # replace newlines in message so it lines up correct @@ -1297,71 +1255,76 @@ function Get-ConciseViewPositionMessage { } if ($err.FullyQualifiedErrorId -eq 'NativeCommandErrorMessage' -or $err.FullyQualifiedErrorId -eq 'NativeCommandError') { - $err.Exception.Message + return ""${errorColor}$($err.Exception.Message)${resetcolor}"" } - else - { - $myinv = $err.InvocationInfo - if ($ErrorView -eq 'DetailedView') { - return (Get-Error | Out-String) - } - elseif ($ErrorView -eq 'ConciseView') { - $posmsg = Get-ConciseViewPositionMessage - } - elseif ($myinv -and ($myinv.MyCommand -or ($err.CategoryInfo.Category -ne 'ParserError'))) { - $posmsg = $myinv.PositionMessage - } else { - $posmsg = '' - } - if ($posmsg -ne '') - { - $posmsg = $newline + $posmsg - } + $myinv = $err.InvocationInfo + if ($ErrorView -eq 'DetailedView') { + $message = Get-Error | Out-String + return ""${errorColor}${message}${resetcolor}"" + } - if ($err.PSMessageDetails) { - $posmsg = ' : ' + $err.PSMessageDetails + $posmsg - } + if ($ErrorView -eq 'CategoryView') { + $message = $err.CategoryInfo.GetMessage() + return ""${errorColor}${message}${resetcolor}"" + } - if ($ErrorView -eq 'ConciseView') { - return $posmsg - } + $posmsg = '' + if ($ErrorView -eq 'ConciseView') { + $posmsg = Get-ConciseViewPositionMessage + } + elseif ($myinv -and ($myinv.MyCommand -or ($err.CategoryInfo.Category -ne 'ParserError'))) { + $posmsg = $myinv.PositionMessage + } - $indent = 4 + if ($posmsg -ne '') { + $posmsg = $newline + $posmsg + } - $errorCategoryMsg = $err.ErrorCategory_Message + if ($err.PSMessageDetails) { + $posmsg = ' : ' + $err.PSMessageDetails + $posmsg + } - if ($null -ne $errorCategoryMsg) - { - $indentString = '+ CategoryInfo : ' + $err.ErrorCategory_Message - } - else - { - $indentString = '+ CategoryInfo : ' + $err.CategoryInfo + if ($ErrorView -eq 'ConciseView') { + if ($err.PSMessageDetails) { + $posmsg = ""${errorColor}${posmsg}"" } + return $posmsg + } - $posmsg += $newline + $indentString + $indent = 4 - $indentString = ""+ FullyQualifiedErrorId : "" + $err.FullyQualifiedErrorId - $posmsg += $newline + $indentString + $errorCategoryMsg = $err.ErrorCategory_Message - $originInfo = $err.OriginInfo + if ($null -ne $errorCategoryMsg) + { + $indentString = '+ CategoryInfo : ' + $err.ErrorCategory_Message + } + else + { + $indentString = '+ CategoryInfo : ' + $err.CategoryInfo + } - if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName)) - { - $indentString = ""+ PSComputerName : "" + $originInfo.PSComputerName - $posmsg += $newline + $indentString - } + $posmsg += $newline + $indentString - if ($ErrorView -eq 'CategoryView') { - $err.CategoryInfo.GetMessage() - } - elseif (! $err.ErrorDetails -or ! $err.ErrorDetails.Message) { - $err.Exception.Message + $posmsg - } else { - $err.ErrorDetails.Message + $posmsg - } + $indentString = ""+ FullyQualifiedErrorId : "" + $err.FullyQualifiedErrorId + $posmsg += $newline + $indentString + + $originInfo = $err.OriginInfo + + if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName)) + { + $indentString = ""+ PSComputerName : "" + $originInfo.PSComputerName + $posmsg += $newline + $indentString } + + $finalMsg = if ($err.ErrorDetails.Message) { + $err.ErrorDetails.Message + $posmsg + } else { + $err.Exception.Message + $posmsg + } + + ""${errorColor}${finalMsg}${resetcolor}"" ") .EndEntry() .EndControl()); @@ -2082,37 +2045,37 @@ private static IEnumerable ViewsOf_System_Management_Autom .AddItemScriptBlock(@"""$($_.FileInfo.Executable)$($_.FileInfo.Executable.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "FileInfo.Executable") .AddItemScriptBlock(@"""$([string]::Join(',',$_.FileInfo.Extension.Keys))""", label: "FileInfo.Extension") .AddItemScriptBlock(@"""$($_.Foreground.Black)$($_.Foreground.Black.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.Black") + .AddItemScriptBlock(@"""$($_.Foreground.BrightBlack)$($_.Foreground.BrightBlack.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.BrightBlack") .AddItemScriptBlock(@"""$($_.Foreground.White)$($_.Foreground.White.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.White") - .AddItemScriptBlock(@"""$($_.Foreground.DarkGray)$($_.Foreground.DarkGray.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.DarkGray") - .AddItemScriptBlock(@"""$($_.Foreground.LightGray)$($_.Foreground.LightGray.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.LightGray") + .AddItemScriptBlock(@"""$($_.Foreground.BrightWhite)$($_.Foreground.BrightWhite.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.BrightWhite") .AddItemScriptBlock(@"""$($_.Foreground.Red)$($_.Foreground.Red.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.Red") - .AddItemScriptBlock(@"""$($_.Foreground.LightRed)$($_.Foreground.LightRed.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.LightRed") + .AddItemScriptBlock(@"""$($_.Foreground.BrightRed)$($_.Foreground.BrightRed.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.BrightRed") .AddItemScriptBlock(@"""$($_.Foreground.Magenta)$($_.Foreground.Magenta.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.Magenta") - .AddItemScriptBlock(@"""$($_.Foreground.LightMagenta)$($_.Foreground.LightMagenta.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.LightMagenta") + .AddItemScriptBlock(@"""$($_.Foreground.BrightMagenta)$($_.Foreground.BrightMagenta.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.BrightMagenta") .AddItemScriptBlock(@"""$($_.Foreground.Blue)$($_.Foreground.Blue.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.Blue") - .AddItemScriptBlock(@"""$($_.Foreground.LightBlue)$($_.Foreground.LightBlue.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.LightBlue") + .AddItemScriptBlock(@"""$($_.Foreground.BrightBlue)$($_.Foreground.BrightBlue.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.BrightBlue") .AddItemScriptBlock(@"""$($_.Foreground.Cyan)$($_.Foreground.Cyan.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.Cyan") - .AddItemScriptBlock(@"""$($_.Foreground.LightCyan)$($_.Foreground.LightCyan.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.LightCyan") + .AddItemScriptBlock(@"""$($_.Foreground.BrightCyan)$($_.Foreground.BrightCyan.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.BrightCyan") .AddItemScriptBlock(@"""$($_.Foreground.Green)$($_.Foreground.Green.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.Green") - .AddItemScriptBlock(@"""$($_.Foreground.LightGreen)$($_.Foreground.LightGreen.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.LightGreen") + .AddItemScriptBlock(@"""$($_.Foreground.BrightGreen)$($_.Foreground.BrightGreen.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.BrightGreen") .AddItemScriptBlock(@"""$($_.Foreground.Yellow)$($_.Foreground.Yellow.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.Yellow") - .AddItemScriptBlock(@"""$($_.Foreground.LightYellow)$($_.Foreground.LightYellow.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.LightYellow") + .AddItemScriptBlock(@"""$($_.Foreground.BrightYellow)$($_.Foreground.BrightYellow.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Foreground.BrightYellow") .AddItemScriptBlock(@"""$($_.Background.Black)$($_.Background.Black.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.Black") + .AddItemScriptBlock(@"""$($_.Background.BrightBlack)$($_.Background.BrightBlack.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.BrightBlack") .AddItemScriptBlock(@"""$($_.Background.White)$($_.Background.White.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.White") - .AddItemScriptBlock(@"""$($_.Background.DarkGray)$($_.Background.DarkGray.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.DarkGray") - .AddItemScriptBlock(@"""$($_.Background.LightGray)$($_.Background.LightGray.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.LightGray") + .AddItemScriptBlock(@"""$($_.Background.BrightWhite)$($_.Background.BrightWhite.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.BrightWhite") .AddItemScriptBlock(@"""$($_.Background.Red)$($_.Background.Red.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.Red") - .AddItemScriptBlock(@"""$($_.Background.LightRed)$($_.Background.LightRed.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.LightRed") + .AddItemScriptBlock(@"""$($_.Background.BrightRed)$($_.Background.BrightRed.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.BrightRed") .AddItemScriptBlock(@"""$($_.Background.Magenta)$($_.Background.Magenta.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.Magenta") - .AddItemScriptBlock(@"""$($_.Background.LightMagenta)$($_.Background.LightMagenta.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.LightMagenta") + .AddItemScriptBlock(@"""$($_.Background.BrightMagenta)$($_.Background.BrightMagenta.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.BrightMagenta") .AddItemScriptBlock(@"""$($_.Background.Blue)$($_.Background.Blue.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.Blue") - .AddItemScriptBlock(@"""$($_.Background.LightBlue)$($_.Background.LightBlue.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.LightBlue") + .AddItemScriptBlock(@"""$($_.Background.BrightBlue)$($_.Background.BrightBlue.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.BrightBlue") .AddItemScriptBlock(@"""$($_.Background.Cyan)$($_.Background.Cyan.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.Cyan") - .AddItemScriptBlock(@"""$($_.Background.LightCyan)$($_.Background.LightCyan.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.LightCyan") + .AddItemScriptBlock(@"""$($_.Background.BrightCyan)$($_.Background.BrightCyan.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.BrightCyan") .AddItemScriptBlock(@"""$($_.Background.Green)$($_.Background.Green.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.Green") - .AddItemScriptBlock(@"""$($_.Background.LightGreen)$($_.Background.LightGreen.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.LightGreen") + .AddItemScriptBlock(@"""$($_.Background.BrightGreen)$($_.Background.BrightGreen.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.BrightGreen") .AddItemScriptBlock(@"""$($_.Background.Yellow)$($_.Background.Yellow.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.Yellow") - .AddItemScriptBlock(@"""$($_.Background.LightYellow)$($_.Background.LightYellow.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.LightYellow") + .AddItemScriptBlock(@"""$($_.Background.BrightYellow)$($_.Background.BrightYellow.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Background.BrightYellow") .EndEntry() .EndList()); } @@ -2185,46 +2148,46 @@ private static IEnumerable ViewsOf_System_Management_Autom ListControl.Create() .StartEntry() .AddItemScriptBlock(@"""$($_.Black)$($_.Black.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Black") + .AddItemScriptBlock(@"""$($_.BrightBlack)$($_.BrightBlack.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightBlack") .AddItemScriptBlock(@"""$($_.White)$($_.White.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "White") - .AddItemScriptBlock(@"""$($_.DarkGray)$($_.DarkGray.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "DarkGray") - .AddItemScriptBlock(@"""$($_.LightGray)$($_.LightGray.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "LightGray") + .AddItemScriptBlock(@"""$($_.BrightWhite)$($_.BrightWhite.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightWhite") .AddItemScriptBlock(@"""$($_.Red)$($_.Red.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Red") - .AddItemScriptBlock(@"""$($_.LightRed)$($_.LightRed.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "LightRed") + .AddItemScriptBlock(@"""$($_.BrightRed)$($_.BrightRed.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightRed") .AddItemScriptBlock(@"""$($_.Magenta)$($_.Magenta.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Magenta") - .AddItemScriptBlock(@"""$($_.LightMagenta)$($_.LightMagenta.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "LightMagenta") + .AddItemScriptBlock(@"""$($_.BrightMagenta)$($_.BrightMagenta.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightMagenta") .AddItemScriptBlock(@"""$($_.Blue)$($_.Blue.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Blue") - .AddItemScriptBlock(@"""$($_.LightBlue)$($_.LightBlue.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "LightBlue") + .AddItemScriptBlock(@"""$($_.BrightBlue)$($_.BrightBlue.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightBlue") .AddItemScriptBlock(@"""$($_.Cyan)$($_.Cyan.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Cyan") - .AddItemScriptBlock(@"""$($_.LightCyan)$($_.LightCyan.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "LightCyan") + .AddItemScriptBlock(@"""$($_.BrightCyan)$($_.BrightCyan.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightCyan") .AddItemScriptBlock(@"""$($_.Green)$($_.Green.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Green") - .AddItemScriptBlock(@"""$($_.LightGreen)$($_.LightGreen.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "LightGreen") + .AddItemScriptBlock(@"""$($_.BrightGreen)$($_.BrightGreen.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightGreen") .AddItemScriptBlock(@"""$($_.Yellow)$($_.Yellow.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Yellow") - .AddItemScriptBlock(@"""$($_.LightYellow)$($_.LightYellow.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "LightYellow") + .AddItemScriptBlock(@"""$($_.BrightYellow)$($_.BrightYellow.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightYellow") .EndEntry() .EndList()); } private static IEnumerable ViewsOf_System_Management_Automation_PSStyleBackgroundColor() { - yield return new FormatViewDefinition("System.Management.Automation.PSStyle+ForegroundColor", + yield return new FormatViewDefinition("System.Management.Automation.PSStyle+BackgroundColor", ListControl.Create() .StartEntry() .AddItemScriptBlock(@"""$($_.Black)$($_.Black.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Black") + .AddItemScriptBlock(@"""$($_.BrightBlack)$($_.BrightBlack.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightBlack") .AddItemScriptBlock(@"""$($_.White)$($_.White.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "White") - .AddItemScriptBlock(@"""$($_.DarkGray)$($_.DarkGray.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "DarkGray") - .AddItemScriptBlock(@"""$($_.LightGray)$($_.LightGray.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "LightGray") + .AddItemScriptBlock(@"""$($_.BrightWhite)$($_.BrightWhite.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightWhite") .AddItemScriptBlock(@"""$($_.Red)$($_.Red.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Red") - .AddItemScriptBlock(@"""$($_.LightRed)$($_.LightRed.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "LightRed") + .AddItemScriptBlock(@"""$($_.BrightRed)$($_.BrightRed.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightRed") .AddItemScriptBlock(@"""$($_.Magenta)$($_.Magenta.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Magenta") - .AddItemScriptBlock(@"""$($_.LightMagenta)$($_.LightMagenta.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "LightMagenta") + .AddItemScriptBlock(@"""$($_.BrightMagenta)$($_.BrightMagenta.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightMagenta") .AddItemScriptBlock(@"""$($_.Blue)$($_.Blue.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Blue") - .AddItemScriptBlock(@"""$($_.LightBlue)$($_.LightBlue.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "LightBlue") + .AddItemScriptBlock(@"""$($_.BrightBlue)$($_.BrightBlue.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightBlue") .AddItemScriptBlock(@"""$($_.Cyan)$($_.Cyan.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Cyan") - .AddItemScriptBlock(@"""$($_.LightCyan)$($_.LightCyan.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "LightCyan") + .AddItemScriptBlock(@"""$($_.BrightCyan)$($_.BrightCyan.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightCyan") .AddItemScriptBlock(@"""$($_.Green)$($_.Green.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Green") - .AddItemScriptBlock(@"""$($_.LightGreen)$($_.LightGreen.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "LightGreen") + .AddItemScriptBlock(@"""$($_.BrightGreen)$($_.BrightGreen.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightGreen") .AddItemScriptBlock(@"""$($_.Yellow)$($_.Yellow.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "Yellow") - .AddItemScriptBlock(@"""$($_.LightYellow)$($_.LightYellow.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "LightYellow") + .AddItemScriptBlock(@"""$($_.BrightYellow)$($_.BrightYellow.Replace(""""`e"""",'`e'))$($PSStyle.Reset)""", label: "BrightYellow") .EndEntry() .EndList()); } diff --git a/src/System.Management.Automation/FormatAndOutput/common/BaseOutputtingCommand.cs b/src/System.Management.Automation/FormatAndOutput/common/BaseOutputtingCommand.cs index 136b7a26ef5..3ab591a7af4 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/BaseOutputtingCommand.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/BaseOutputtingCommand.cs @@ -128,10 +128,7 @@ private bool ProcessObject(PSObject so) } // instantiate the cache if not done yet - if (_cache == null) - { - _cache = new FormattedObjectsCache(this.LineOutput.RequiresBuffering); - } + _cache ??= new FormattedObjectsCache(this.LineOutput.RequiresBuffering); // no need for formatting, just process the object FormatStartData formatStart = o as FormatStartData; @@ -552,7 +549,8 @@ private void ProcessOutOfBandPayload(FormatEntryData fed) } else { - _lo.WriteLine(rte.text); + // Write out raw text without any changes to it. + _lo.WriteRawText(rte.text); } return; diff --git a/src/System.Management.Automation/FormatAndOutput/common/ComplexWriter.cs b/src/System.Management.Automation/FormatAndOutput/common/ComplexWriter.cs index 187775d3072..0ffc713cb92 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/ComplexWriter.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/ComplexWriter.cs @@ -6,8 +6,10 @@ using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Globalization; +using System.Management.Automation; using System.Management.Automation.Internal; using System.Text; +using System.Text.RegularExpressions; namespace Microsoft.PowerShell.Commands.Internal.Format { @@ -146,7 +148,7 @@ private void WriteToScreen() int indentationAbsoluteValue = (firstLineIndentation > 0) ? firstLineIndentation : -firstLineIndentation; if (indentationAbsoluteValue >= usefulWidth) { - // valu too big, we reset it to zero + // value too big, we reset it to zero firstLineIndentation = 0; } @@ -237,10 +239,7 @@ internal IndentationStackFrame(IndentationManager mgr) public void Dispose() { - if (_mgr != null) - { - _mgr.RemoveStackFrame(); - } + _mgr?.RemoveStackFrame(); } private readonly IndentationManager _mgr; @@ -353,27 +352,58 @@ static StringManipulationHelper() private static IEnumerable GetWords(string s) { StringBuilder sb = new StringBuilder(); - GetWordsResult result = new GetWordsResult(); + StringBuilder vtSeqs = null; + Dictionary vtRanges = null; + + var valueStrDec = new ValueStringDecorated(s); + if (valueStrDec.IsDecorated) + { + vtSeqs = new StringBuilder(); + vtRanges = valueStrDec.EscapeSequenceRanges; + } + bool wordHasVtSeqs = false; for (int i = 0; i < s.Length; i++) { - // Soft hyphen = \u00AD - Should break, and add a hyphen if needed. If not needed for a break, hyphen should be absent - if (s[i] == ' ' || s[i] == '\t' || s[i] == s_softHyphen) + if (vtRanges?.TryGetValue(i, out int len) == true) { - result.Word = sb.ToString(); - sb.Clear(); - result.Delim = new string(s[i], 1); + var vtSpan = s.AsSpan(i, len); + sb.Append(vtSpan); + vtSeqs.Append(vtSpan); - yield return result; + wordHasVtSeqs = true; + i += len - 1; + continue; + } + + string delimiter = null; + if (s[i] == ' ' || s[i] == '\t' || s[i] == s_softHyphen) + { + // Soft hyphen = \u00AD - Should break, and add a hyphen if needed. + // If not needed for a break, hyphen should be absent. + delimiter = new string(s[i], 1); } - // Non-breaking space = \u00A0 - ideally shouldn't wrap - // Hard hyphen = \u2011 - Should not break else if (s[i] == s_hardHyphen || s[i] == s_nonBreakingSpace) { - result.Word = sb.ToString(); - sb.Clear(); - result.Delim = string.Empty; + // Non-breaking space = \u00A0 - ideally shouldn't wrap. + // Hard hyphen = \u2011 - Should not break. + delimiter = string.Empty; + } + + if (delimiter is not null) + { + if (wordHasVtSeqs && !sb.EndsWith(PSStyle.Instance.Reset)) + { + sb.Append(PSStyle.Instance.Reset); + } + + var result = new GetWordsResult() + { + Word = sb.ToString(), + Delim = delimiter + }; + sb.Clear().Append(vtSeqs); yield return result; } else @@ -382,10 +412,23 @@ private static IEnumerable GetWords(string s) } } - result.Word = sb.ToString(); - result.Delim = string.Empty; + if (wordHasVtSeqs) + { + if (sb.Length == vtSeqs.Length) + { + // This indicates 'sb' only contains all VT sequences, which may happen when the string ends with a word delimiter. + // For a word that contains VT sequence only, it's the same as an empty string to the formatting system, + // because nothing will actually be rendered. + // So, we use an empty string in this case to avoid unneeded string allocations. + sb.Clear(); + } + else if (!sb.EndsWith(PSStyle.Instance.Reset)) + { + sb.Append(PSStyle.Instance.Reset); + } + } - yield return result; + yield return new GetWordsResult() { Word = sb.ToString(), Delim = string.Empty }; } internal static StringCollection GenerateLines(DisplayCells displayCells, string val, int firstLineLen, int followingLinesLen) @@ -412,9 +455,9 @@ private static StringCollection GenerateLinesWithoutWordWrap(DisplayCells displa } // break string on newlines and process each line separately - string[] lines = SplitLines(val); + List lines = SplitLines(val); - for (int k = 0; k < lines.Length; k++) + for (int k = 0; k < lines.Count; k++) { string currentLine = lines[k]; @@ -451,7 +494,7 @@ private static StringCollection GenerateLinesWithoutWordWrap(DisplayCells displa { // we are not at the end of the string, select a sub string // that would fit in the remaining display length - int charactersToAdd = displayCells.GetHeadSplitLength(currentLine, offset, currentDisplayLen); + int charactersToAdd = displayCells.TruncateTail(currentLine, offset, currentDisplayLen); if (charactersToAdd <= 0) { @@ -465,7 +508,7 @@ private static StringCollection GenerateLinesWithoutWordWrap(DisplayCells displa else { // of the given length, add it to the accumulator - accumulator.AddLine(currentLine.Substring(offset, charactersToAdd)); + accumulator.AddLine(currentLine.VtSubstring(offset, charactersToAdd)); } // increase the offset by the # of characters added @@ -474,7 +517,7 @@ private static StringCollection GenerateLinesWithoutWordWrap(DisplayCells displa else { // we reached the last (partial) line, we add it all - accumulator.AddLine(currentLine.Substring(offset)); + accumulator.AddLine(currentLine.VtSubstring(offset)); break; } } @@ -530,9 +573,9 @@ private static StringCollection GenerateLinesWithWordWrap(DisplayCells displayCe } // break string on newlines and process each line separately - string[] lines = SplitLines(val); + List lines = SplitLines(val); - for (int k = 0; k < lines.Length; k++) + for (int k = 0; k < lines.Count; k++) { if (lines[k] == null || displayCells.Length(lines[k]) <= firstLineLen) { @@ -545,28 +588,34 @@ private static StringCollection GenerateLinesWithWordWrap(DisplayCells displayCe int lineWidth = firstLineLen; bool firstLine = true; StringBuilder singleLine = new StringBuilder(); + string resetStr = PSStyle.Instance.Reset; foreach (GetWordsResult word in GetWords(lines[k])) { string wordToAdd = word.Word; + string suffix = null; // Handle soft hyphen - if (word.Delim == s_softHyphen.ToString()) + if (word.Delim.Length == 1 && word.Delim[0] == s_softHyphen) { - int wordWidthWithHyphen = displayCells.Length(wordToAdd) + displayCells.Length(s_softHyphen.ToString()); + int wordWidthWithHyphen = displayCells.Length(wordToAdd) + displayCells.Length(s_softHyphen); // Add hyphen only if necessary if (wordWidthWithHyphen == spacesLeft) { - wordToAdd += "-"; + suffix = "-"; } } - else + else if (!string.IsNullOrEmpty(word.Delim)) { - if (!string.IsNullOrEmpty(word.Delim)) - { - wordToAdd += word.Delim; - } + suffix = word.Delim; + } + + if (suffix is not null) + { + wordToAdd = wordToAdd.EndsWith(resetStr) + ? wordToAdd.Insert(wordToAdd.Length - resetStr.Length, suffix) + : wordToAdd + suffix; } int wordWidth = displayCells.Length(wordToAdd); @@ -591,15 +640,35 @@ private static StringCollection GenerateLinesWithWordWrap(DisplayCells displayCe // Word is wider than a single line if (wordWidth > lineWidth) { - foreach (char c in wordToAdd) + Dictionary vtRanges = null; + StringBuilder vtSeqs = null; + + var valueStrDec = new ValueStringDecorated(wordToAdd); + if (valueStrDec.IsDecorated) { - char charToAdd = c; - int charWidth = displayCells.Length(c); + vtSeqs = new StringBuilder(); + vtRanges = valueStrDec.EscapeSequenceRanges; + } - // corner case: we have a two cell character and the current - // display length is one. - // add a single cell arbitrary character instead of the original - // one and keep going + bool hasEscSeqs = false; + for (int i = 0; i < wordToAdd.Length; i++) + { + if (vtRanges?.TryGetValue(i, out int len) == true) + { + var vtSpan = wordToAdd.AsSpan(i, len); + singleLine.Append(vtSpan); + vtSeqs.Append(vtSpan); + + hasEscSeqs = true; + i += len - 1; + continue; + } + + char charToAdd = wordToAdd[i]; + int charWidth = displayCells.Length(charToAdd); + + // Corner case: we have a two cell character and the current display length is one. + // Add a single cell arbitrary character instead of the original one and keep going. if (charWidth > lineWidth) { charToAdd = '?'; @@ -608,9 +677,13 @@ private static StringCollection GenerateLinesWithWordWrap(DisplayCells displayCe if (charWidth > spacesLeft) { + if (hasEscSeqs && !singleLine.EndsWith(resetStr)) + { + singleLine.Append(resetStr); + } + retVal.Add(singleLine.ToString()); - singleLine.Clear(); - singleLine.Append(charToAdd); + singleLine.Clear().Append(vtSeqs).Append(charToAdd); if (firstLine) { @@ -632,8 +705,7 @@ private static StringCollection GenerateLinesWithWordWrap(DisplayCells displayCe if (wordWidth > spacesLeft) { retVal.Add(singleLine.ToString()); - singleLine.Clear(); - singleLine.Append(wordToAdd); + singleLine.Clear().Append(wordToAdd); if (firstLine) { @@ -663,49 +735,77 @@ private static StringCollection GenerateLinesWithWordWrap(DisplayCells displayCe /// /// String to split. /// String array with the values. - internal static string[] SplitLines(string s) + internal static List SplitLines(string s) { - if (string.IsNullOrEmpty(s)) - return new string[1] { s }; + if (string.IsNullOrEmpty(s) || !s.Contains('\n')) + { + return new List(capacity: 1) { s?.Replace("\r", string.Empty) }; + } StringBuilder sb = new StringBuilder(); + List list = new List(); - foreach (char c in s) + StringBuilder vtSeqs = null; + Dictionary vtRanges = null; + + var valueStrDec = new ValueStringDecorated(s); + if (valueStrDec.IsDecorated) { - if (c != '\r') - sb.Append(c); + vtSeqs = new StringBuilder(); + vtRanges = valueStrDec.EscapeSequenceRanges; } - return sb.ToString().Split(s_newLineChar); - } - -#if false - internal static string StripNewLines (string s) - { - if (string.IsNullOrEmpty (s)) - return s; - - string[] lines = SplitLines (s); + bool hasVtSeqs = false; + for (int i = 0; i < s.Length; i++) + { + if (vtRanges?.TryGetValue(i, out int len) == true) + { + var vtSpan = s.AsSpan(i, len); + sb.Append(vtSpan); + vtSeqs.Append(vtSpan); - if (lines.Length == 0) - return null; + hasVtSeqs = true; + i += len - 1; + continue; + } - if (lines.Length == 1) - return lines[0]; + char c = s[i]; + if (c == '\n') + { + if (hasVtSeqs && !sb.EndsWith(PSStyle.Instance.Reset)) + { + sb.Append(PSStyle.Instance.Reset); + } - StringBuilder sb = new StringBuilder (); + list.Add(sb.ToString()); + sb.Clear().Append(vtSeqs); + } + else if (c != '\r') + { + sb.Append(c); + } + } - for (int k = 0; k < lines.Length; k++) + if (hasVtSeqs) { - if (k == 0) - sb.Append (lines[k]); - else - sb.Append (" " + lines[k]); + if (sb.Length == vtSeqs.Length) + { + // This indicates 'sb' only contains all VT sequences, which may happen when the string ends with '\n'. + // For a sub-string that contains VT sequence only, it's the same as an empty string to the formatting + // system, because nothing will actually be rendered. + // So, we use an empty string in this case to avoid unneeded string allocations. + sb.Clear(); + } + else if (!sb.EndsWith(PSStyle.Instance.Reset)) + { + sb.Append(PSStyle.Instance.Reset); + } } - return sb.ToString (); + list.Add(sb.ToString()); + return list; } -#endif + internal static string TruncateAtNewLine(string s) { if (string.IsNullOrEmpty(s)) diff --git a/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/FormatTable.cs b/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/FormatTable.cs index 43cab48d003..91d0fabf44c 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/FormatTable.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/FormatTable.cs @@ -19,7 +19,7 @@ namespace System.Management.Automation.Runspaces { /// /// This exception is used by Formattable constructor to indicate errors - /// occured during construction time. + /// occurred during construction time. /// [Serializable] [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "FormatTable")] @@ -70,7 +70,7 @@ public FormatTableLoadException(string message, Exception innerException) /// time. /// /// - /// The errors that occured + /// The errors that occurred. /// internal FormatTableLoadException(ConcurrentBag loadErrors) : base(StringUtil.Format(FormatAndOutXmlLoadingStrings.FormatTableLoadErrors)) diff --git a/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/displayDescriptionData.cs b/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/displayDescriptionData.cs index f126dddcadc..0190a31ddc9 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/displayDescriptionData.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/displayDescriptionData.cs @@ -361,6 +361,7 @@ internal sealed class FieldPropertyToken : PropertyTokenBase internal sealed class FieldFormattingDirective { internal string formatString = null; // optional + internal bool isTable = false; } #endregion Elementary Tokens @@ -886,7 +887,7 @@ internal static EntrySelectedBy Get(List references) { if (tr.conditionToken != null) { - if (result.SelectionCondition == null) result.SelectionCondition = new List(); + result.SelectionCondition ??= new List(); result.SelectionCondition.Add(new DisplayEntry(tr.conditionToken)); continue; @@ -895,7 +896,7 @@ internal static EntrySelectedBy Get(List references) if (tr is TypeGroupReference) continue; - if (result.TypeNames == null) result.TypeNames = new List(); + result.TypeNames ??= new List(); result.TypeNames.Add(tr.name); } diff --git a/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/displayDescriptionData_List.cs b/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/displayDescriptionData_List.cs index 6025acfff44..4ac470d288c 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/displayDescriptionData_List.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/displayDescriptionData_List.cs @@ -208,8 +208,7 @@ public List SelectedBy { get { - if (EntrySelectedBy == null) - EntrySelectedBy = new EntrySelectedBy { TypeNames = new List() }; + EntrySelectedBy ??= new EntrySelectedBy { TypeNames = new List() }; return EntrySelectedBy.TypeNames; } } diff --git a/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/displayDescriptionData_Wide.cs b/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/displayDescriptionData_Wide.cs index 4f87998fd1e..220446bfe17 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/displayDescriptionData_Wide.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/DisplayDatabase/displayDescriptionData_Wide.cs @@ -187,8 +187,7 @@ public List SelectedBy { get { - if (EntrySelectedBy == null) - EntrySelectedBy = new EntrySelectedBy { TypeNames = new List() }; + EntrySelectedBy ??= new EntrySelectedBy { TypeNames = new List() }; return EntrySelectedBy.TypeNames; } } diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Complex.cs b/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Complex.cs index c1645b09ad5..77aa2da6e4d 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Complex.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Complex.cs @@ -283,10 +283,7 @@ private void ExecuteFormatTokenList(TraversalInfo level, { // Since it is a leaf node we just consider it an empty string and go // on with formatting - if (val == null) - { - val = string.Empty; - } + val ??= string.Empty; FieldFormattingDirective fieldFormattingDirective = null; StringFormatError formatErrorObject = null; diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Table.cs b/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Table.cs index bfd3291dd46..b0efa32dc1f 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Table.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Table.cs @@ -233,10 +233,7 @@ private TableHeaderInfo GenerateTableHeaderInfoFromProperties(PSObject so) ci.propertyName = (string)key; } - if (ci.propertyName == null) - { - ci.propertyName = this.activeAssociationList[k].ResolvedExpression.ToString(); - } + ci.propertyName ??= this.activeAssociationList[k].ResolvedExpression.ToString(); // set the width of the table if (a.OriginatingParameter != null) @@ -391,10 +388,7 @@ private List GetActiveTableRowDefinition(TableControlBod } } - if (matchingRowDefinition == null) - { - matchingRowDefinition = match.BestMatch as TableRowDefinition; - } + matchingRowDefinition ??= match.BestMatch as TableRowDefinition; if (matchingRowDefinition == null) { @@ -412,10 +406,7 @@ private List GetActiveTableRowDefinition(TableControlBod } } - if (matchingRowDefinition == null) - { - matchingRowDefinition = match.BestMatch as TableRowDefinition; - } + matchingRowDefinition ??= match.BestMatch as TableRowDefinition; } } @@ -483,6 +474,12 @@ private TableRowEntry GenerateTableRowEntryFromFromProperties(PSObject so, int e directive = activeAssociationList[k].OriginatingParameter.GetEntry(FormatParameterDefinitionKeys.FormatStringEntryKey) as FieldFormattingDirective; } + if (directive is null) + { + directive = new FieldFormattingDirective(); + directive.isTable = true; + } + fpf.propertyValue = this.GetExpressionDisplayValue(so, enumerationLimit, this.activeAssociationList[k].ResolvedExpression, directive); tre.formatPropertyFieldList.Add(fpf); } diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormattingObjectsDeserializer.cs b/src/System.Management.Automation/FormatAndOutput/common/FormattingObjectsDeserializer.cs index 962cf6f5ff1..f26a20ae6a9 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/FormattingObjectsDeserializer.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/FormattingObjectsDeserializer.cs @@ -325,9 +325,7 @@ internal WriteStreamType DeserializeWriteStreamTypeMemberVariable(PSObject so) internal FormatInfoData DeserializeObject(PSObject so) { FormatInfoData fid = FormatInfoDataClassFactory.CreateInstance(so, this); - - if (fid != null) - fid.Deserialize(so, this); + fid?.Deserialize(so, this); return fid; } diff --git a/src/System.Management.Automation/FormatAndOutput/common/ILineOutput.cs b/src/System.Management.Automation/FormatAndOutput/common/ILineOutput.cs index 8c672b62063..3c200664636 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/ILineOutput.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/ILineOutput.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Management.Automation; @@ -14,60 +15,112 @@ namespace Microsoft.PowerShell.Commands.Internal.Format { /// /// Base class providing support for string manipulation. - /// This class is a tear off class provided by the LineOutput class - /// - /// Assumptions (in addition to the assumptions made for LineOutput): - /// - characters map to one or more character cells - /// - /// NOTE: we provide a base class that is valid for devices that have a - /// 1:1 mapping between a UNICODE character and a display cell. + /// This class is a tear off class provided by the LineOutput class. /// internal class DisplayCells { - internal virtual int Length(string str) + /// + /// Calculate the buffer cell length of the given string. + /// + /// String that may contain VT escape sequences. + /// Number of buffer cells the string needs to take. + internal int Length(string str) { return Length(str, 0); } + /// + /// Calculate the buffer cell length of the given string. + /// + /// String that may contain VT escape sequences. + /// + /// When the string doesn't contain VT sequences, it's the starting index. + /// When the string contains VT sequences, it means starting from the 'n-th' char that doesn't belong to a escape sequence. + /// Number of buffer cells the string needs to take. internal virtual int Length(string str, int offset) { - int length = 0; + if (string.IsNullOrEmpty(str)) + { + return 0; + } - foreach (char c in str) + var valueStrDec = new ValueStringDecorated(str); + if (valueStrDec.IsDecorated) { - length += LengthInBufferCells(c); + str = valueStrDec.ToString(OutputRendering.PlainText); } - return length - offset; - } + int length = 0; + for (; offset < str.Length; offset++) + { + length += CharLengthInBufferCells(str[offset]); + } - internal virtual int Length(char character) { return 1; } + return length; + } - internal virtual int GetHeadSplitLength(string str, int displayCells) + /// + /// Calculate the buffer cell length of the given character. + /// + /// + /// Number of buffer cells the character needs to take. + internal virtual int Length(char character) { - return GetHeadSplitLength(str, 0, displayCells); + return CharLengthInBufferCells(character); } - internal virtual int GetHeadSplitLength(string str, int offset, int displayCells) + /// + /// Truncate from the tail of the string. + /// + /// String that may contain VT escape sequences. + /// Number of buffer cells to fit in. + /// Number of non-escape-sequence characters from head of the string that can fit in the space. + internal int TruncateTail(string str, int displayCells) { - int len = str.Length - offset; - return (len < displayCells) ? len : displayCells; + return TruncateTail(str, offset: 0, displayCells); } - internal virtual int GetTailSplitLength(string str, int displayCells) + /// + /// Truncate from the tail of the string. + /// + /// String that may contain VT escape sequences. + /// + /// When the string doesn't contain VT sequences, it's the starting index. + /// When the string contains VT sequences, it means starting from the 'n-th' char that doesn't belong to a escape sequence. + /// Number of buffer cells to fit in. + /// Number of non-escape-sequence characters from head of the string that can fit in the space. + internal int TruncateTail(string str, int offset, int displayCells) { - return GetTailSplitLength(str, 0, displayCells); + var valueStrDec = new ValueStringDecorated(str); + if (valueStrDec.IsDecorated) + { + str = valueStrDec.ToString(OutputRendering.PlainText); + } + + return GetFitLength(str, offset, displayCells, startFromHead: true); } - internal virtual int GetTailSplitLength(string str, int offset, int displayCells) + /// + /// Truncate from the head of the string. + /// + /// String that may contain VT escape sequences. + /// Number of buffer cells to fit in. + /// Number of non-escape-sequence characters from head of the string that should be skipped. + internal int TruncateHead(string str, int displayCells) { - int len = str.Length - offset; - return (len < displayCells) ? len : displayCells; + var valueStrDec = new ValueStringDecorated(str); + if (valueStrDec.IsDecorated) + { + str = valueStrDec.ToString(OutputRendering.PlainText); + } + + int tailCount = GetFitLength(str, offset: 0, displayCells, startFromHead: false); + return str.Length - tailCount; } #region Helpers - protected static int LengthInBufferCells(char c) + protected static int CharLengthInBufferCells(char c) { // The following is based on http://www.cl.cam.ac.uk/~mgk25/c/wcwidth.c // which is derived from https://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt @@ -94,25 +147,26 @@ protected static int LengthInBufferCells(char c) /// Given a string and a number of display cells, it computes how many /// characters would fit starting from the beginning or end of the string. /// - /// String to be displayed. + /// String to be displayed, which doesn't contain any VT sequences. /// Offset inside the string. /// Number of display cells. - /// If true compute from the head (i.e. k++) else from the tail (i.e. k--). + /// If true compute from the head (i.e. k++) else from the tail (i.e. k--). /// Number of characters that would fit. - protected int GetSplitLengthInternalHelper(string str, int offset, int displayCells, bool head) + protected int GetFitLength(string str, int offset, int displayCells, bool startFromHead) { int filledDisplayCellsCount = 0; // number of cells that are filled in int charactersAdded = 0; // number of characters that fit int currCharDisplayLen; // scratch variable - int k = (head) ? offset : str.Length - 1; - int kFinal = (head) ? str.Length - 1 : offset; + int k = startFromHead ? offset : str.Length - 1; + int kFinal = startFromHead ? str.Length - 1 : offset; while (true) { - if ((head && (k > kFinal)) || ((!head) && (k < kFinal))) + if ((startFromHead && k > kFinal) || (!startFromHead && k < kFinal)) { break; } + // compute the cell number for the current character currCharDisplayLen = this.Length(str[k]); @@ -121,6 +175,7 @@ protected int GetSplitLengthInternalHelper(string str, int offset, int displayCe // if we added this character it would not fit, we cannot continue break; } + // keep adding, we fit filledDisplayCellsCount += currCharDisplayLen; charactersAdded++; @@ -132,13 +187,13 @@ protected int GetSplitLengthInternalHelper(string str, int offset, int displayCe break; } - k = (head) ? (k + 1) : (k - 1); + k = startFromHead ? (k + 1) : (k - 1); } return charactersAdded; } - #endregion + #endregion } /// @@ -195,6 +250,13 @@ internal virtual void ExecuteBufferPlayBack(DoPlayBackCall playback) { } /// internal abstract void WriteLine(string s); + /// + /// Write a line of string as raw text to the output device, with no change to the string. + /// For example, keeping VT escape sequences intact in it. + /// + /// The raw text to be written to the device. + internal virtual void WriteRawText(string s) => WriteLine(s); + internal WriteStreamType WriteStream { get; @@ -325,10 +387,10 @@ private void WriteLineInternal(string val, int cols) } // check for line breaks - string[] lines = StringManipulationHelper.SplitLines(val); + List lines = StringManipulationHelper.SplitLines(val); // process the substrings as separate lines - for (int k = 0; k < lines.Length; k++) + for (int k = 0; k < lines.Count; k++) { // compute the display length of the string int displayLength = _displayCells.Length(lines[k]); @@ -354,11 +416,11 @@ private void WriteLineInternal(string val, int cols) { // the string is still too long to fit, write the first cols characters // and go back for more wraparound - int splitLen = _displayCells.GetHeadSplitLength(s, cols); - WriteLineInternal(s.Substring(0, splitLen), cols); + int headCount = _displayCells.TruncateTail(s, cols); + WriteLineInternal(s.VtSubstring(0, headCount), cols); // chop off the first fieldWidth characters, already printed - s = s.Substring(splitLen); + s = s.VtSubstring(headCount); if (_displayCells.Length(s) <= cols) { // if we fit, print the tail of the string and we are done @@ -376,7 +438,7 @@ private void WriteLineInternal(string val, int cols) /// Implementation of the ILineOutput interface accepting an instance of a /// TextWriter abstract class. /// - internal class TextWriterLineOutput : LineOutput + internal sealed class TextWriterLineOutput : LineOutput { #region ILineOutput methods @@ -412,9 +474,17 @@ internal override int RowNumber /// internal override void WriteLine(string s) { - CheckStopProcessing(); + WriteRawText(PSHostUserInterface.GetOutputString(s, isHost: false)); + } - s = PSHostUserInterface.GetOutputString(s, isHost: false); + /// + /// Write a raw text by delegating to the writer underneath, with no change to the text. + /// For example, keeping VT escape sequences intact in it. + /// + /// The raw text to be written to the device. + internal override void WriteRawText(string s) + { + CheckStopProcessing(); if (_suppressNewline) { @@ -425,6 +495,7 @@ internal override void WriteLine(string s) _writer.WriteLine(s); } } + #endregion /// diff --git a/src/System.Management.Automation/FormatAndOutput/common/ListWriter.cs b/src/System.Management.Automation/FormatAndOutput/common/ListWriter.cs index b5c97c3d85d..7598c6b2797 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/ListWriter.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/ListWriter.cs @@ -2,10 +2,12 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Management.Automation; using System.Management.Automation.Internal; +using System.Text; namespace Microsoft.PowerShell.Commands.Internal.Format { @@ -30,6 +32,11 @@ internal class ListWriter /// private int _columnWidth = 0; + /// + /// A cached string builder used within this type to reduce creation of temporary strings. + /// + private readonly StringBuilder _cachedBuilder = new(); + /// /// /// Names of the properties to display. @@ -88,19 +95,20 @@ internal void Initialize(string[] propertyNames, int screenColumnWidth, DisplayC for (int k = 0; k < propertyNames.Length; k++) { + string propertyName = propertyNames[k]; if (propertyNameCellCounts[k] < _propertyLabelsDisplayLength) { // shorter than the max, add padding - _propertyLabels[k] = propertyNames[k] + StringUtil.Padding(_propertyLabelsDisplayLength - propertyNameCellCounts[k]); + _propertyLabels[k] = propertyName + StringUtil.Padding(_propertyLabelsDisplayLength - propertyNameCellCounts[k]); } else if (propertyNameCellCounts[k] > _propertyLabelsDisplayLength) { // longer than the max, clip - _propertyLabels[k] = propertyNames[k].Substring(0, dc.GetHeadSplitLength(propertyNames[k], _propertyLabelsDisplayLength)); + _propertyLabels[k] = propertyName.VtSubstring(0, dc.TruncateTail(propertyName, _propertyLabelsDisplayLength)); } else { - _propertyLabels[k] = propertyNames[k]; + _propertyLabels[k] = propertyName; } _propertyLabels[k] += Separator; @@ -169,16 +177,15 @@ internal void WriteProperties(string[] values, LineOutput lo) /// LineOutput interface to write to. private void WriteProperty(int k, string propertyValue, LineOutput lo) { - if (propertyValue == null) - propertyValue = string.Empty; + propertyValue ??= string.Empty; // make sure we honor embedded newlines - string[] lines = StringManipulationHelper.SplitLines(propertyValue); + List lines = StringManipulationHelper.SplitLines(propertyValue); // padding to use in the lines after the first string padding = null; - for (int i = 0; i < lines.Length; i++) + for (int i = 0; i < lines.Count; i++) { string prependString = null; @@ -186,8 +193,7 @@ private void WriteProperty(int k, string propertyValue, LineOutput lo) prependString = _propertyLabels[k]; else { - if (padding == null) - padding = StringUtil.Padding(_propertyLabelsDisplayLength); + padding ??= StringUtil.Padding(_propertyLabelsDisplayLength); prependString = padding; } @@ -202,11 +208,10 @@ private void WriteProperty(int k, string propertyValue, LineOutput lo) /// /// String to add to the left. /// Line to print. - /// LineOuput to write to. + /// LineOutput to write to. private void WriteSingleLineHelper(string prependString, string line, LineOutput lo) { - if (line == null) - line = string.Empty; + line ??= string.Empty; // compute the width of the field for the value string (in screen cells) int fieldCellCount = _columnWidth - _propertyLabelsDisplayLength; @@ -214,20 +219,52 @@ private void WriteSingleLineHelper(string prependString, string line, LineOutput // split the lines StringCollection sc = StringManipulationHelper.GenerateLines(lo.DisplayCells, line, fieldCellCount, fieldCellCount); - // padding to use in the lines after the first - string padding = StringUtil.Padding(_propertyLabelsDisplayLength); + // The padding to use in the lines after the first. + string headPadding = null; + + // The VT style used for the list label. + string style = PSStyle.Instance.Formatting.FormatAccent; + string reset = PSStyle.Instance.Reset; // display the string collection for (int k = 0; k < sc.Count; k++) { + string str = sc[k]; + _cachedBuilder.Clear(); + if (k == 0) { - lo.WriteLine(PSStyle.Instance.Formatting.FormatAccent + prependString + PSStyle.Instance.Reset + sc[k]); + if (string.IsNullOrWhiteSpace(prependString) || style == string.Empty) + { + // - Sometimes 'prependString' is just padding white spaces, and we don't + // need to add formatting escape sequences in such a case. + // - Otherwise, if the style is an empty string, then the user has chosen + // to not apply a style to the list label. + _cachedBuilder.Append(prependString).Append(str); + } + else + { + // Apply the style to the list label. + _cachedBuilder + .Append(style) + .Append(prependString) + .Append(reset) + .Append(str); + } } else { - lo.WriteLine(padding + PSStyle.Instance.Formatting.FormatAccent + PSStyle.Instance.Reset + sc[k]); + // Lazily calculate the padding to use for the subsequent lines as it's quite often that only the first line exists. + headPadding ??= StringUtil.Padding(_propertyLabelsDisplayLength); + _cachedBuilder.Append(headPadding).Append(str); } + + if (str.Contains(ValueStringDecorated.ESC) && !str.EndsWith(reset)) + { + _cachedBuilder.Append(reset); + } + + lo.WriteLine(_cachedBuilder.ToString()); } } diff --git a/src/System.Management.Automation/FormatAndOutput/common/OutputManager.cs b/src/System.Management.Automation/FormatAndOutput/common/OutputManager.cs index 41ea611f198..a551ef89bed 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/OutputManager.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/OutputManager.cs @@ -92,19 +92,14 @@ internal override void ProcessRecord() internal override void EndProcessing() { // shut down only if we ever processed a pipeline object - if (_mgr != null) - _mgr.ShutDown(); + _mgr?.ShutDown(); } internal override void StopProcessing() { lock (_syncRoot) { - if (_lo != null) - { - _lo.StopProcessing(); - } - + _lo?.StopProcessing(); _isStopped = true; } } diff --git a/src/System.Management.Automation/FormatAndOutput/common/PSStyle.cs b/src/System.Management.Automation/FormatAndOutput/common/PSStyle.cs index 9f6b74bdc1d..3a5dcb36f0b 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/PSStyle.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/PSStyle.cs @@ -35,7 +35,7 @@ public enum ProgressView /// Classic rendering of progress. Classic = 1, } - + #region PSStyle /// /// Contains configuration for how PowerShell renders text. @@ -53,79 +53,79 @@ public sealed class ForegroundColor public string Black { get; } = "\x1b[30m"; /// - /// Gets the color blue. + /// Gets the color red. /// - public string Blue { get; } = "\x1b[34m"; + public string Red { get; } = "\x1b[31m"; /// - /// Gets the color cyan. + /// Gets the color green. /// - public string Cyan { get; } = "\x1b[36m"; + public string Green { get; } = "\x1b[32m"; /// - /// Gets the color dark gray. + /// Gets the color yellow. /// - public string DarkGray { get; } = "\x1b[90m"; + public string Yellow { get; } = "\x1b[33m"; /// - /// Gets the color green. + /// Gets the color blue. /// - public string Green { get; } = "\x1b[32m"; + public string Blue { get; } = "\x1b[34m"; /// - /// Gets the color light blue. + /// Gets the color magenta. /// - public string LightBlue { get; } = "\x1b[94m"; + public string Magenta { get; } = "\x1b[35m"; /// - /// Gets the color light cyan. + /// Gets the color cyan. /// - public string LightCyan { get; } = "\x1b[96m"; + public string Cyan { get; } = "\x1b[36m"; /// - /// Gets the color light gray. + /// Gets the color white. /// - public string LightGray { get; } = "\x1b[97m"; + public string White { get; } = "\x1b[37m"; /// - /// Gets the color light green. + /// Gets the color bright black. /// - public string LightGreen { get; } = "\x1b[92m"; + public string BrightBlack { get; } = "\x1b[90m"; /// - /// Gets the color light magenta. + /// Gets the color bright red. /// - public string LightMagenta { get; } = "\x1b[95m"; + public string BrightRed { get; } = "\x1b[91m"; /// - /// Gets the color light red. + /// Gets the color bright green. /// - public string LightRed { get; } = "\x1b[91m"; + public string BrightGreen { get; } = "\x1b[92m"; /// - /// Gets the color light yellow. + /// Gets the color bright yellow. /// - public string LightYellow { get; } = "\x1b[93m"; + public string BrightYellow { get; } = "\x1b[93m"; /// - /// Gets the color magenta. + /// Gets the color bright blue. /// - public string Magenta { get; } = "\x1b[35m"; + public string BrightBlue { get; } = "\x1b[94m"; /// - /// Gets the color read. + /// Gets the color bright magenta. /// - public string Red { get; } = "\x1b[31m"; + public string BrightMagenta { get; } = "\x1b[95m"; /// - /// Gets the color white. + /// Gets the color bright cyan. /// - public string White { get; } = "\x1b[37m"; + public string BrightCyan { get; } = "\x1b[96m"; /// - /// Gets the color yellow. + /// Gets the color bright white. /// - public string Yellow { get; } = "\x1b[33m"; + public string BrightWhite { get; } = "\x1b[97m"; /// /// Set as RGB (Red, Green, Blue). @@ -168,79 +168,79 @@ public sealed class BackgroundColor public string Black { get; } = "\x1b[40m"; /// - /// Gets the color blue. + /// Gets the color red. /// - public string Blue { get; } = "\x1b[44m"; + public string Red { get; } = "\x1b[41m"; /// - /// Gets the color cyan. + /// Gets the color green. /// - public string Cyan { get; } = "\x1b[46m"; + public string Green { get; } = "\x1b[42m"; /// - /// Gets the color dark gray. + /// Gets the color yellow. /// - public string DarkGray { get; } = "\x1b[100m"; + public string Yellow { get; } = "\x1b[43m"; /// - /// Gets the color green. + /// Gets the color blue. /// - public string Green { get; } = "\x1b[42m"; + public string Blue { get; } = "\x1b[44m"; /// - /// Gets the color light blue. + /// Gets the color magenta. /// - public string LightBlue { get; } = "\x1b[104m"; + public string Magenta { get; } = "\x1b[45m"; /// - /// Gets the color light cyan. + /// Gets the color cyan. /// - public string LightCyan { get; } = "\x1b[106m"; + public string Cyan { get; } = "\x1b[46m"; /// - /// Gets the color light gray. + /// Gets the color white. /// - public string LightGray { get; } = "\x1b[107m"; + public string White { get; } = "\x1b[47m"; /// - /// Gets the color light green. + /// Gets the color bright black. /// - public string LightGreen { get; } = "\x1b[102m"; + public string BrightBlack { get; } = "\x1b[100m"; /// - /// Gets the color light magenta. + /// Gets the color bright red. /// - public string LightMagenta { get; } = "\x1b[105m"; + public string BrightRed { get; } = "\x1b[101m"; /// - /// Gets the color light red. + /// Gets the color bright green. /// - public string LightRed { get; } = "\x1b[101m"; + public string BrightGreen { get; } = "\x1b[102m"; /// - /// Gets the color light yellow. + /// Gets the color bright yellow. /// - public string LightYellow { get; } = "\x1b[103m"; + public string BrightYellow { get; } = "\x1b[103m"; /// - /// Gets the color magenta. + /// Gets the color bright blue. /// - public string Magenta { get; } = "\x1b[45m"; + public string BrightBlue { get; } = "\x1b[104m"; /// - /// Gets the color read. + /// Gets the color bright magenta. /// - public string Red { get; } = "\x1b[41m"; + public string BrightMagenta { get; } = "\x1b[105m"; /// - /// Gets the color white. + /// Gets the color bright cyan. /// - public string White { get; } = "\x1b[47m"; + public string BrightCyan { get; } = "\x1b[106m"; /// - /// Gets the color yellow. + /// Gets the color bright white. /// - public string Yellow { get; } = "\x1b[43m"; + public string BrightWhite { get; } = "\x1b[107m"; /// /// The color set as RGB (Red, Green, Blue). @@ -365,7 +365,7 @@ public string Error get => _error; set => _error = ValidateNoContent(value); } - + private string _error = "\x1b[31;1m"; /// @@ -397,7 +397,7 @@ public string Debug { get => _debug; set => _debug = ValidateNoContent(value); - } + } private string _debug = "\x1b[33;1m"; } @@ -467,6 +467,16 @@ public void Add(string extension, string decoration) _extensionDictionary.Add(ValidateExtension(extension), ValidateNoContent(decoration)); } + /// + /// Add new extension and decoration to dictionary without validation. + /// + /// Extension to add. + /// ANSI string value to add. + internal void AddWithoutValidation(string extension, string decoration) + { + _extensionDictionary.Add(extension, decoration); + } + /// /// Remove an extension from dictionary. /// @@ -543,19 +553,19 @@ public FileInfoFormatting() Extension = new FileExtensionDictionary(); // archives - Extension.Add(".zip", "\x1b[31;1m"); - Extension.Add(".tgz", "\x1b[31;1m"); - Extension.Add(".gz", "\x1b[31;1m"); - Extension.Add(".tar", "\x1b[31;1m"); - Extension.Add(".nupkg", "\x1b[31;1m"); - Extension.Add(".cab", "\x1b[31;1m"); - Extension.Add(".7z", "\x1b[31;1m"); + Extension.AddWithoutValidation(".zip", "\x1b[31;1m"); + Extension.AddWithoutValidation(".tgz", "\x1b[31;1m"); + Extension.AddWithoutValidation(".gz", "\x1b[31;1m"); + Extension.AddWithoutValidation(".tar", "\x1b[31;1m"); + Extension.AddWithoutValidation(".nupkg", "\x1b[31;1m"); + Extension.AddWithoutValidation(".cab", "\x1b[31;1m"); + Extension.AddWithoutValidation(".7z", "\x1b[31;1m"); // powershell - Extension.Add(".ps1", "\x1b[33;1m"); - Extension.Add(".psd1", "\x1b[33;1m"); - Extension.Add(".psm1", "\x1b[33;1m"); - Extension.Add(".ps1xml", "\x1b[33;1m"); + Extension.AddWithoutValidation(".ps1", "\x1b[33;1m"); + Extension.AddWithoutValidation(".psd1", "\x1b[33;1m"); + Extension.AddWithoutValidation(".psm1", "\x1b[33;1m"); + Extension.AddWithoutValidation(".ps1xml", "\x1b[33;1m"); } } @@ -688,7 +698,12 @@ private PSStyle() private static string ValidateNoContent(string text) { - var decorartedString = new StringDecorated(text); + if (text is null) + { + throw new ArgumentNullException(nameof(text)); + } + + var decorartedString = new ValueStringDecorated(text); if (decorartedString.ContentLength > 0) { throw new ArgumentException(string.Format(PSStyleStrings.TextContainsContent, decorartedString.ToString(OutputRendering.PlainText))); diff --git a/src/System.Management.Automation/FormatAndOutput/common/StringDecorated.cs b/src/System.Management.Automation/FormatAndOutput/common/StringDecorated.cs index 03a360ff5c1..6fa725c574f 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/StringDecorated.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/StringDecorated.cs @@ -3,6 +3,7 @@ #nullable enable +using System.Collections.Generic; using System.Text.RegularExpressions; namespace System.Management.Automation.Internal @@ -20,10 +21,7 @@ private string PlainText { get { - if (_plaintextcontent == null) - { - _plaintextcontent = ValueStringDecorated.AnsiRegex.Replace(_text, string.Empty); - } + _plaintextcontent ??= ValueStringDecorated.AnsiRegex.Replace(_text, string.Empty); return _plaintextcontent; } @@ -87,22 +85,53 @@ internal struct ValueStringDecorated private readonly bool _isDecorated; private readonly string _text; private string? _plaintextcontent; + private Dictionary? _vtRanges; private string PlainText { get { - if (_plaintextcontent == null) - { - _plaintextcontent = AnsiRegex.Replace(_text, string.Empty); - } + _plaintextcontent ??= AnsiRegex.Replace(_text, string.Empty); return _plaintextcontent; } } + // graphics/color mode ESC[1;2;...m + private const string GraphicsRegex = @"(\x1b\[\d+(;\d+)*m)"; + + // CSI escape sequences + private const string CsiRegex = @"(\x1b\[\?\d+[hl])"; + + // Hyperlink escape sequences. Note: '.*?' makes '.*' do non-greedy match. + private const string HyperlinkRegex = @"(\x1b\]8;;.*?\x1b\\)"; + // replace regex with .NET 6 API once available - internal static readonly Regex AnsiRegex = new Regex(@"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])", RegexOptions.Compiled); + internal static readonly Regex AnsiRegex = new Regex($"{GraphicsRegex}|{CsiRegex}|{HyperlinkRegex}", RegexOptions.Compiled); + + /// + /// Get the ranges of all escape sequences in the text. + /// + /// + /// A dictionary with the key being the starting index of an escape sequence, + /// and the value being the length of the escape sequence. + /// + internal Dictionary? EscapeSequenceRanges + { + get + { + if (_isDecorated && _vtRanges is null) + { + _vtRanges = new Dictionary(); + foreach (Match match in AnsiRegex.Matches(_text)) + { + _vtRanges.Add(match.Index, match.Length); + } + } + + return _vtRanges; + } + } /// /// Initializes a new instance of the struct. @@ -112,7 +141,8 @@ public ValueStringDecorated(string text) { _text = text; _isDecorated = text.Contains(ESC); - _plaintextcontent = null; + _plaintextcontent = _isDecorated ? null : text; + _vtRanges = null; } /// diff --git a/src/System.Management.Automation/FormatAndOutput/common/TableWriter.cs b/src/System.Management.Automation/FormatAndOutput/common/TableWriter.cs index fb5ef0bf930..e21ef5b20d9 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/TableWriter.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/TableWriter.cs @@ -42,8 +42,6 @@ private sealed class ScreenInfo private ScreenInfo _si; - private const char ESC = '\u001b'; - private List _header; internal static int ComputeWideViewBestItemsPerRowFit(int stringLen, int screenColumns) @@ -153,9 +151,12 @@ internal int GenerateHeader(string[] values, LineOutput lo) } else if (_header != null) { + string style = PSStyle.Instance.Formatting.TableHeader; + string reset = PSStyle.Instance.Reset; + foreach (string line in _header) { - lo.WriteLine(PSStyle.Instance.Formatting.TableHeader + line + PSStyle.Instance.Reset); + lo.WriteLine(style == string.Empty ? line : style + line + reset); } return _header.Count; @@ -228,6 +229,9 @@ internal void GenerateRow(string[] values, LineOutput lo, bool multiLine, ReadOn } } + string style = PSStyle.Instance.Formatting.TableHeader; + string reset = PSStyle.Instance.Reset; + if (multiLine) { foreach (string line in GenerateTableRow(values, currentAlignment, lo.DisplayCells)) @@ -235,7 +239,7 @@ internal void GenerateRow(string[] values, LineOutput lo, bool multiLine, ReadOn generatedRows?.Add(line); if (isHeader) { - lo.WriteLine(PSStyle.Instance.Formatting.TableHeader + line + PSStyle.Instance.Reset); + lo.WriteLine(style == string.Empty ? line : style + line + reset); } else { @@ -249,7 +253,7 @@ internal void GenerateRow(string[] values, LineOutput lo, bool multiLine, ReadOn generatedRows?.Add(line); if (isHeader) { - lo.WriteLine(PSStyle.Instance.Formatting.TableHeader + line + PSStyle.Instance.Reset); + lo.WriteLine(style == string.Empty ? line : style + line + reset); } else { @@ -457,8 +461,10 @@ private string GenerateRow(string[] values, ReadOnlySpan alignment, Display } } - sb.Append(GenerateRowField(values[k], _si.columnInfo[k].width, alignment[k], dc, addPadding)); - if (values[k] is not null && values[k].Contains(ESC)) + string rowField = GenerateRowField(values[k], _si.columnInfo[k].width, alignment[k], dc, addPadding); + sb.Append(rowField); + + if (rowField is not null && rowField.Contains(ValueStringDecorated.ESC) && !rowField.AsSpan().TrimEnd().EndsWith(PSStyle.Instance.Reset)) { // Reset the console output if the content of this column contains ESC sb.Append(PSStyle.Instance.Reset); @@ -472,14 +478,12 @@ private static string GenerateRowField(string val, int width, int alignment, Dis { // make sure the string does not have any embedded in it string s = StringManipulationHelper.TruncateAtNewLine(val); - - string currentValue = s; - int currentValueDisplayLength = dc.Length(currentValue); + int currentValueDisplayLength = dc.Length(s); if (currentValueDisplayLength < width) { // the string is shorter than the width of the column - // need to pad with with blanks to reach the desired width + // need to pad with blanks to reach the desired width int padCount = width - currentValueDisplayLength; switch (alignment) { @@ -531,18 +535,10 @@ private static string GenerateRowField(string val, int width, int alignment, Dis case TextAlignment.Right: { // get from "abcdef" to "...f" - int tailCount = dc.GetTailSplitLength(s, truncationDisplayLength); - s = s.Substring(s.Length - tailCount); - s = PSObjectHelper.Ellipsis + s; - } - - break; - - case TextAlignment.Center: - { - // get from "abcdef" to "a..." - s = s.Substring(0, dc.GetHeadSplitLength(s, truncationDisplayLength)); - s += PSObjectHelper.Ellipsis; + s = s.VtSubstring( + startOffset: dc.TruncateHead(s, truncationDisplayLength), + prependStr: PSObjectHelper.EllipsisStr, + appendStr: null); } break; @@ -551,8 +547,11 @@ private static string GenerateRowField(string val, int width, int alignment, Dis { // left align is the default // get from "abcdef" to "a..." - s = s.Substring(0, dc.GetHeadSplitLength(s, truncationDisplayLength)); - s += PSObjectHelper.Ellipsis; + s = s.VtSubstring( + startOffset: 0, + length: dc.TruncateTail(s, truncationDisplayLength), + prependStr: null, + appendStr: PSObjectHelper.EllipsisStr); } break; @@ -561,23 +560,12 @@ private static string GenerateRowField(string val, int width, int alignment, Dis else { // not enough space for the ellipsis, just truncate at the width - int len = width; - switch (alignment) { case TextAlignment.Right: { // get from "abcdef" to "f" - int tailCount = dc.GetTailSplitLength(s, len); - s = s.Substring(s.Length - tailCount, tailCount); - } - - break; - - case TextAlignment.Center: - { - // get from "abcdef" to "a" - s = s.Substring(0, dc.GetHeadSplitLength(s, len)); + s = s.VtSubstring(startOffset: dc.TruncateHead(s, width)); } break; @@ -586,7 +574,7 @@ private static string GenerateRowField(string val, int width, int alignment, Dis { // left align is the default // get from "abcdef" to "a" - s = s.Substring(0, dc.GetHeadSplitLength(s, len)); + s = s.VtSubstring(startOffset: 0, length: dc.TruncateTail(s, width)); } break; diff --git a/src/System.Management.Automation/FormatAndOutput/common/Utilities/MshObjectUtil.cs b/src/System.Management.Automation/FormatAndOutput/common/Utilities/MshObjectUtil.cs index 7760e86b13d..ce3590520a5 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/Utilities/MshObjectUtil.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/Utilities/MshObjectUtil.cs @@ -26,6 +26,7 @@ internal static class PSObjectHelper #endregion tracer internal const char Ellipsis = '\u2026'; + internal const string EllipsisStr = "\u2026"; internal static string PSObjectIsOfExactType(Collection typeNames) { @@ -207,8 +208,9 @@ private static string GetObjectName(object x, PSPropertyExpressionFactory expres /// Expression factory to create PSPropertyExpression. /// Limit on IEnumerable enumeration. /// Stores errors during string conversion. + /// Determine if to format floating point numbers using current culture. /// String representation. - internal static string SmartToString(PSObject so, PSPropertyExpressionFactory expressionFactory, int enumerationLimit, StringFormatError formatErrorObject) + internal static string SmartToString(PSObject so, PSPropertyExpressionFactory expressionFactory, int enumerationLimit, StringFormatError formatErrorObject, bool formatFloat = false) { if (so == null) return string.Empty; @@ -293,7 +295,23 @@ internal static string SmartToString(PSObject so, PSPropertyExpressionFactory ex return sb.ToString(); } - // take care of the case there is no base object + if (formatFloat && so.BaseObject is not null) + { + // format numbers using the current culture + if (so.BaseObject is double dbl) + { + return dbl.ToString("F"); + } + else if (so.BaseObject is float f) + { + return f.ToString("F"); + } + else if (so.BaseObject is decimal d) + { + return d.ToString("F"); + } + } + return so.ToString(); } catch (Exception e) when (e is ExtendedTypeSystemException || e is InvalidOperationException) @@ -332,43 +350,49 @@ internal static string FormatField(FieldFormattingDirective directive, object va StringFormatError formatErrorObject, PSPropertyExpressionFactory expressionFactory) { PSObject so = PSObjectHelper.AsPSObject(val); - if (directive != null && !string.IsNullOrEmpty(directive.formatString)) + bool isTable = false; + if (directive is not null) { - // we have a formatting directive, apply it - // NOTE: with a format directive, we do not make any attempt - // to deal with IEnumerable - try + isTable = directive.isTable; + if (!string.IsNullOrEmpty(directive.formatString)) { - // use some heuristics to determine if we have "composite formatting" - // 2004/11/16-JonN This is heuristic but should be safe enough - if (directive.formatString.Contains("{0") || directive.formatString.Contains('}')) + // we have a formatting directive, apply it + // NOTE: with a format directive, we do not make any attempt + // to deal with IEnumerable + try { - // we do have it, just use it - return string.Format(CultureInfo.CurrentCulture, directive.formatString, so); + // use some heuristics to determine if we have "composite formatting" + // 2004/11/16-JonN This is heuristic but should be safe enough + if (directive.formatString.Contains("{0") || directive.formatString.Contains('}')) + { + // we do have it, just use it + return string.Format(CultureInfo.CurrentCulture, directive.formatString, so); + } + // we fall back to the PSObject's IFormattable.ToString() + // pass a null IFormatProvider + return so.ToString(directive.formatString, formatProvider: null); } - // we fall back to the PSObject's IFormattable.ToString() - // pass a null IFormatProvider - return so.ToString(directive.formatString, null); - } - catch (Exception e) // 2004/11/17-JonN This covers exceptions thrown in - // string.Format and PSObject.ToString(). - // I think we can swallow these. - { - // NOTE: we catch all the exceptions, since we do not know - // what the underlying object access would throw - if (formatErrorObject != null) + catch (Exception e) // 2004/11/17-JonN This covers exceptions thrown in + // string.Format and PSObject.ToString(). + // I think we can swallow these. { - formatErrorObject.sourceObject = so; - formatErrorObject.exception = e; - formatErrorObject.formatString = directive.formatString; - return string.Empty; + // NOTE: we catch all the exceptions, since we do not know + // what the underlying object access would throw + if (formatErrorObject is not null) + { + formatErrorObject.sourceObject = so; + formatErrorObject.exception = e; + formatErrorObject.formatString = directive.formatString; + return string.Empty; + } } } } + // we do not have a formatting directive or we failed the formatting (fallback) // but we did not report as an error; // this call would deal with IEnumerable if the object implements it - return PSObjectHelper.SmartToString(so, expressionFactory, enumerationLimit, formatErrorObject); + return PSObjectHelper.SmartToString(so, expressionFactory, enumerationLimit, formatErrorObject, isTable); } private static PSMemberSet MaskDeserializedAndGetStandardMembers(PSObject so) diff --git a/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs b/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs index 68921ddb0e8..9a675824e59 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs @@ -326,15 +326,12 @@ private PSPropertyExpressionResult GetValue(PSObject target, bool eatExceptions) } else { - if (_getValueDynamicSite == null) - { - _getValueDynamicSite = - CallSite>.Create( - PSGetMemberBinder.Get( - _stringValue, - classScope: (Type)null, - @static: false)); - } + _getValueDynamicSite ??= + CallSite>.Create( + PSGetMemberBinder.Get( + _stringValue, + classScope: (Type)null, + @static: false)); result = _getValueDynamicSite.Target.Invoke(_getValueDynamicSite, target); } diff --git a/src/System.Management.Automation/FormatAndOutput/out-console/ConsoleLineOutput.cs b/src/System.Management.Automation/FormatAndOutput/out-console/ConsoleLineOutput.cs index f3ed3ba6189..e8379d3f321 100644 --- a/src/System.Management.Automation/FormatAndOutput/out-console/ConsoleLineOutput.cs +++ b/src/System.Management.Automation/FormatAndOutput/out-console/ConsoleLineOutput.cs @@ -1,11 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -// NOTE: define this if you want to test the output on US machine and ASCII -// characters -//#define TEST_MULTICELL_ON_SINGLE_CELL_LOCALE - using System; +using System.Collections.Generic; using System.Collections.Specialized; using System.Management.Automation; using System.Management.Automation.Internal; @@ -17,70 +14,12 @@ namespace Microsoft.PowerShell.Commands.Internal.Format { -#if TEST_MULTICELL_ON_SINGLE_CELL_LOCALE - - /// - /// Test class to provide easily overridable behavior for testing on US machines - /// using US data. - /// NOTE: the class just forces any uppercase letter [A-Z] to be prepended - /// with an underscore (e.g. "A" becomes "_A", but "a" stays the same) - /// - internal class DisplayCellsTest : DisplayCells - { - internal override int Length(string str, int offset) - { - int len = 0; - for (int k = offset; k < str.Length; k++) - { - len += this.Length(str[k]); - } - - return len; - } - - internal override int Length(char character) - { - if (character >= 'A' && character <= 'Z') - return 2; - return 1; - } - - internal override int GetHeadSplitLength(string str, int offset, int displayCells) - { - return GetSplitLengthInternalHelper(str, offset, displayCells, true); - } - - internal override int GetTailSplitLength(string str, int offset, int displayCells) - { - return GetSplitLengthInternalHelper(str, offset, displayCells, false); - } - - internal string GenerateTestString(string str) - { - StringBuilder sb = new StringBuilder(); - for (int k = 0; k < str.Length; k++) - { - char ch = str[k]; - if (this.Length(ch) == 2) - { - sb.Append('_'); - } - - sb.Append(ch); - } - - return sb.ToString(); - } - - } -#endif - /// /// Tear off class. /// - internal class DisplayCellsPSHost : DisplayCells + internal class DisplayCellsHost : DisplayCells { - internal DisplayCellsPSHost(PSHostRawUserInterface rawUserInterface) + internal DisplayCellsHost(PSHostRawUserInterface rawUserInterface) { _rawUserInterface = rawUserInterface; } @@ -99,30 +38,26 @@ internal override int Length(string str, int offset) try { - return _rawUserInterface.LengthInBufferCells(str, offset); - } - catch - { - // thrown when external host rawui is not implemented, in which case - // we will fallback to the default value. - } + var valueStrDec = new ValueStringDecorated(str); + if (valueStrDec.IsDecorated) + { + str = valueStrDec.ToString(OutputRendering.PlainText); + } - return str.Length - offset; - } + int length = 0; + for (; offset < str.Length; offset++) + { + length += _rawUserInterface.LengthInBufferCells(str[offset]); + } - internal override int Length(string str) - { - try - { - return _rawUserInterface.LengthInBufferCells(str); + return length; } catch { // thrown when external host rawui is not implemented, in which case // we will fallback to the default value. + return base.Length(str, offset); } - - return string.IsNullOrEmpty(str) ? 0 : str.Length; } internal override int Length(char character) @@ -135,19 +70,8 @@ internal override int Length(char character) { // thrown when external host rawui is not implemented, in which case // we will fallback to the default value. + return base.Length(character); } - - return 1; - } - - internal override int GetHeadSplitLength(string str, int offset, int displayCells) - { - return GetSplitLengthInternalHelper(str, offset, displayCells, true); - } - - internal override int GetTailSplitLength(string str, int offset, int displayCells) - { - return GetSplitLengthInternalHelper(str, offset, displayCells, false); } private readonly PSHostRawUserInterface _rawUserInterface; @@ -163,6 +87,11 @@ internal sealed class ConsoleLineOutput : LineOutput internal static readonly PSTraceSource tracer = PSTraceSource.GetTracer("ConsoleLineOutput", "ConsoleLineOutput"); #endregion tracer + /// + /// The default buffer cell calculation already works for the PowerShell console host and Visual studio code host. + /// + private static readonly HashSet s_psHost = new(StringComparer.Ordinal) { "ConsoleHost", "Visual Studio Code Host" }; + #region LineOutput implementation /// /// The # of columns is just the width of the screen buffer (not the @@ -241,12 +170,13 @@ internal override DisplayCells DisplayCells get { CheckStopProcessing(); - if (_displayCellsPSHost != null) + if (_displayCellsHost != null) { - return _displayCellsPSHost; + return _displayCellsHost; } + // fall back if we do not have a Msh host specific instance - return _displayCellsPSHost; + return _displayCellsDefault; } } #endregion @@ -254,39 +184,38 @@ internal override DisplayCells DisplayCells /// /// Constructor for the ConsoleLineOutput. /// - /// PSHostUserInterface to wrap. + /// PSHostUserInterface to wrap. /// True if we require prompting for page breaks. /// Error context to throw exceptions. - internal ConsoleLineOutput(PSHostUserInterface hostConsole, bool paging, TerminatingErrorContext errorContext) + internal ConsoleLineOutput(PSHost host, bool paging, TerminatingErrorContext errorContext) { - if (hostConsole == null) - throw PSTraceSource.NewArgumentNullException(nameof(hostConsole)); + if (host == null) + { + throw PSTraceSource.NewArgumentNullException(nameof(host)); + } + if (errorContext == null) + { throw PSTraceSource.NewArgumentNullException(nameof(errorContext)); + } - _console = hostConsole; + _console = host.UI; _errorContext = errorContext; if (paging) { tracer.WriteLine("paging is needed"); - // if we need to do paging, instantiate a prompt handler - // that will take care of the screen interaction + + // If we need to do paging, instantiate a prompt handler that will take care of the screen interaction string promptString = StringUtil.Format(FormatAndOut_out_xxx.ConsoleLineOutput_PagingPrompt); _prompt = new PromptHandler(promptString, this); } - PSHostRawUserInterface raw = _console.RawUI; - if (raw != null) + if (!s_psHost.Contains(host.Name) && _console.RawUI is not null) { - tracer.WriteLine("there is a valid raw interface"); -#if TEST_MULTICELL_ON_SINGLE_CELL_LOCALE - // create a test instance with fake behavior - this._displayCellsPSHost = new DisplayCellsTest(); -#else // set only if we have a valid raw interface - _displayCellsPSHost = new DisplayCellsPSHost(raw); -#endif + tracer.WriteLine("there is a valid raw interface"); + _displayCellsHost = new DisplayCellsHost(_console.RawUI); } // instantiate the helper to do the line processing when ILineOutput.WriteXXX() is called @@ -309,9 +238,6 @@ internal ConsoleLineOutput(PSHostUserInterface hostConsole, bool paging, Termina /// String to write. private void OnWriteLine(string s) { -#if TEST_MULTICELL_ON_SINGLE_CELL_LOCALE - s = ((DisplayCellsTest)this._displayCellsPSHost).GenerateTestString(s); -#endif // Do any default transcription. _console.TranscribeResult(s); @@ -356,10 +282,6 @@ private void OnWriteLine(string s) /// String to write. private void OnWrite(string s) { -#if TEST_MULTICELL_ON_SINGLE_CELL_LOCALE - s = ((DisplayCellsTest)this._displayCellsPSHost).GenerateTestString(s); -#endif - switch (this.WriteStream) { case WriteStreamType.Error: @@ -615,7 +537,7 @@ internal PromptResponse PromptUser(PSHostUserInterface console) /// /// Msh host specific string manipulation helper. /// - private readonly DisplayCells _displayCellsPSHost; + private readonly DisplayCells _displayCellsHost; /// /// Reference to error context to throw Msh exceptions. diff --git a/src/System.Management.Automation/FormatAndOutput/out-console/OutConsole.cs b/src/System.Management.Automation/FormatAndOutput/out-console/OutConsole.cs index 2582a1e68db..8d51f42de8c 100644 --- a/src/System.Management.Automation/FormatAndOutput/out-console/OutConsole.cs +++ b/src/System.Management.Automation/FormatAndOutput/out-console/OutConsole.cs @@ -65,8 +65,7 @@ public OutDefaultCommand() /// protected override void BeginProcessing() { - PSHostUserInterface console = this.Host.UI; - ConsoleLineOutput lineOutput = new ConsoleLineOutput(console, false, new TerminatingErrorContext(this)); + var lineOutput = new ConsoleLineOutput(Host, false, new TerminatingErrorContext(this)); ((OutputManagerInner)this.implementation).LineOutput = lineOutput; @@ -206,8 +205,7 @@ public SwitchParameter Paging /// protected override void BeginProcessing() { - PSHostUserInterface console = this.Host.UI; - ConsoleLineOutput lineOutput = new ConsoleLineOutput(console, _paging, new TerminatingErrorContext(this)); + var lineOutput = new ConsoleLineOutput(Host, _paging, new TerminatingErrorContext(this)); ((OutputManagerInner)this.implementation).LineOutput = lineOutput; base.BeginProcessing(); diff --git a/src/System.Management.Automation/System.Management.Automation.csproj b/src/System.Management.Automation/System.Management.Automation.csproj index 2ebdabb9593..daf6daaca62 100644 --- a/src/System.Management.Automation/System.Management.Automation.csproj +++ b/src/System.Management.Automation/System.Management.Automation.csproj @@ -12,23 +12,30 @@ - + + + - + - - - - - - - - - - + + + + + + + + + + + - + + + @@ -44,7 +51,6 @@ - diff --git a/src/System.Management.Automation/cimSupport/cmdletization/ObjectModelWrapper.cs b/src/System.Management.Automation/cimSupport/cmdletization/ObjectModelWrapper.cs index a840ca527fd..ab6b741280e 100644 --- a/src/System.Management.Automation/cimSupport/cmdletization/ObjectModelWrapper.cs +++ b/src/System.Management.Automation/cimSupport/cmdletization/ObjectModelWrapper.cs @@ -50,10 +50,7 @@ internal void Initialize(PSCmdlet cmdlet, string className, string classVersion, delegate { var disposable = this as IDisposable; - if (disposable != null) - { - disposable.Dispose(); - } + disposable?.Dispose(); }; } } diff --git a/src/System.Management.Automation/engine/ArgumentToVersionTransformationAttribute.cs b/src/System.Management.Automation/engine/ArgumentToVersionTransformationAttribute.cs new file mode 100644 index 00000000000..facaa9e3e1a --- /dev/null +++ b/src/System.Management.Automation/engine/ArgumentToVersionTransformationAttribute.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace System.Management.Automation +{ + /// + /// To make it easier to specify a version, we add some conversions that wouldn't happen otherwise: + /// * A simple integer, i.e. 2; + /// * A string without a dot, i.e. "2". + /// + internal class ArgumentToVersionTransformationAttribute : ArgumentTransformationAttribute + { + /// + public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) + { + object version = PSObject.Base(inputData); + + if (version is string versionStr) + { + if (TryConvertFromString(versionStr, out var convertedVersion)) + { + return convertedVersion; + } + + if (versionStr.Contains('.')) + { + // If the string contains a '.', let the Version constructor handle the conversion. + return inputData; + } + } + + if (version is double) + { + // The conversion to int below is wrong, but the usual conversions will turn + // the double into a string, so just return the original object. + return inputData; + } + + if (LanguagePrimitives.TryConvertTo(version, out var majorVersion)) + { + return new Version(majorVersion, 0); + } + + return inputData; + } + + protected virtual bool TryConvertFromString(string versionString, [NotNullWhen(true)] out Version? version) + { + version = null; + return false; + } + } +} diff --git a/src/System.Management.Automation/engine/Attributes.cs b/src/System.Management.Automation/engine/Attributes.cs index a0411c78df7..9965824a927 100644 --- a/src/System.Management.Automation/engine/Attributes.cs +++ b/src/System.Management.Automation/engine/Attributes.cs @@ -1088,6 +1088,12 @@ public ValidateRangeAttribute(ValidateRangeKind kind) : base() private static void ValidateRange(object element, ValidateRangeKind rangeKind) { + if (element is TimeSpan ts) + { + ValidateTimeSpanRange(ts, rangeKind); + return; + } + Type commonType = GetCommonType(typeof(int), element.GetType()); if (commonType == null) { @@ -1212,6 +1218,59 @@ private void ValidateRange(object element) } } + private static void ValidateTimeSpanRange(TimeSpan element, ValidateRangeKind rangeKind) + { + TimeSpan zero = TimeSpan.Zero; + + switch (rangeKind) + { + case ValidateRangeKind.Positive: + if (zero.CompareTo(element) >= 0) + { + throw new ValidationMetadataException( + "ValidateRangePositiveFailure", + null, + Metadata.ValidateRangePositiveFailure, + element.ToString()); + } + + break; + case ValidateRangeKind.NonNegative: + if (zero.CompareTo(element) > 0) + { + throw new ValidationMetadataException( + "ValidateRangeNonNegativeFailure", + null, + Metadata.ValidateRangeNonNegativeFailure, + element.ToString()); + } + + break; + case ValidateRangeKind.Negative: + if (zero.CompareTo(element) <= 0) + { + throw new ValidationMetadataException( + "ValidateRangeNegativeFailure", + null, + Metadata.ValidateRangeNegativeFailure, + element.ToString()); + } + + break; + case ValidateRangeKind.NonPositive: + if (zero.CompareTo(element) < 0) + { + throw new ValidationMetadataException( + "ValidateRangeNonPositiveFailure", + null, + Metadata.ValidateRangeNonPositiveFailure, + element.ToString()); + } + + break; + } + } + private static Type GetCommonType(Type minType, Type maxType) { Type resultType = null; diff --git a/src/System.Management.Automation/engine/COM/ComTypeInfo.cs b/src/System.Management.Automation/engine/COM/ComTypeInfo.cs index 851325e82ec..eab301db3ca 100644 --- a/src/System.Management.Automation/engine/COM/ComTypeInfo.cs +++ b/src/System.Management.Automation/engine/COM/ComTypeInfo.cs @@ -183,10 +183,7 @@ private void AddProperty(string strName, COM.FUNCDESC funcdesc, int index) _properties[strName] = prop; } - if (prop != null) - { - prop.UpdateFuncDesc(funcdesc, index); - } + prop?.UpdateFuncDesc(funcdesc, index); } private void AddMethod(string strName, int index) @@ -198,10 +195,7 @@ private void AddMethod(string strName, int index) _methods[strName] = method; } - if (method != null) - { - method.AddFuncDesc(index); - } + method?.AddFuncDesc(index); } /// diff --git a/src/System.Management.Automation/engine/CmdletInfo.cs b/src/System.Management.Automation/engine/CmdletInfo.cs index 05b7548f9fc..659ed59978a 100644 --- a/src/System.Management.Automation/engine/CmdletInfo.cs +++ b/src/System.Management.Automation/engine/CmdletInfo.cs @@ -380,11 +380,8 @@ public override ReadOnlyCollection OutputType } } - if (provider == null) - { - // No path argument, so just use the current path to choose the provider. - provider = Context.SessionState.Path.CurrentLocation.Provider; - } + // If no path argument, just use the current path to choose the provider. + provider ??= Context.SessionState.Path.CurrentLocation.Provider; provider.GetOutputTypes(Name, providerTypes); if (providerTypes.Count > 0) diff --git a/src/System.Management.Automation/engine/CmdletParameterBinderController.cs b/src/System.Management.Automation/engine/CmdletParameterBinderController.cs index 8f853610447..ee2fec058cf 100644 --- a/src/System.Management.Automation/engine/CmdletParameterBinderController.cs +++ b/src/System.Management.Automation/engine/CmdletParameterBinderController.cs @@ -202,10 +202,7 @@ internal void BindCommandLineParameters(Collection arg internal void BindCommandLineParametersNoValidation(Collection arguments) { var psCompiledScriptCmdlet = this.Command as PSScriptCmdlet; - if (psCompiledScriptCmdlet != null) - { - psCompiledScriptCmdlet.PrepareForBinding(this.CommandLineParameters); - } + psCompiledScriptCmdlet?.PrepareForBinding(this.CommandLineParameters); InitUnboundArguments(arguments); CommandMetadata cmdletMetadata = _commandMetadata; @@ -270,8 +267,7 @@ internal void BindCommandLineParametersNoValidation(Collection(); + ObsoleteParameterWarningList ??= new List(); ObsoleteParameterWarningList.Add(warningRecord); } diff --git a/src/System.Management.Automation/engine/ComInterop/ComEventsSink.Extended.cs b/src/System.Management.Automation/engine/ComInterop/ComEventsSink.Extended.cs index f52ded959c2..0d63276a3ea 100644 --- a/src/System.Management.Automation/engine/ComInterop/ComEventsSink.Extended.cs +++ b/src/System.Management.Automation/engine/ComInterop/ComEventsSink.Extended.cs @@ -18,10 +18,7 @@ private void Initialize(object rcw, Guid iid) public void AddHandler(int dispid, object func) { ComEventsMethod method = FindMethod(dispid); - if (method == null) - { - method = AddMethod(dispid); - } + method ??= AddMethod(dispid); if (func is Delegate d) { diff --git a/src/System.Management.Automation/engine/ComInterop/ComInvokeBinder.cs b/src/System.Management.Automation/engine/ComInterop/ComInvokeBinder.cs index 6876425acf7..88e4114cd58 100644 --- a/src/System.Management.Automation/engine/ComInterop/ComInvokeBinder.cs +++ b/src/System.Management.Automation/engine/ComInterop/ComInvokeBinder.cs @@ -114,10 +114,7 @@ private ParameterExpression ParamVariantsVariable { get { - if (_paramVariants == null) - { - _paramVariants = Expression.Variable(VariantArray.GetStructType(_args.Length), "paramVariants"); - } + _paramVariants ??= Expression.Variable(VariantArray.GetStructType(_args.Length), "paramVariants"); return _paramVariants; } } @@ -140,10 +137,7 @@ private static Type MarshalType(DynamicMetaObject mo, bool isByRef) if (isByRef) { // Null just means that null was supplied. - if (marshalType == null) - { - marshalType = mo.Expression.Type; - } + marshalType ??= mo.Expression.Type; marshalType = marshalType.MakeByRefType(); } return marshalType; diff --git a/src/System.Management.Automation/engine/ComInterop/ComRuntimeHelpers.cs b/src/System.Management.Automation/engine/ComInterop/ComRuntimeHelpers.cs index 39038595abc..5bd086874a6 100644 --- a/src/System.Management.Automation/engine/ComInterop/ComRuntimeHelpers.cs +++ b/src/System.Management.Automation/engine/ComInterop/ComRuntimeHelpers.cs @@ -322,7 +322,7 @@ internal static void InitVariantForObject(object obj, ref Variant variant) Debug.Assert(obj != null); // GetNativeVariantForObject is very expensive for values that marshal as VT_DISPATCH - // also is is extremely common scenario when object at hand is an RCW. + // also is extremely common scenario when object at hand is an RCW. // Therefore we are going to test for IDispatch before defaulting to GetNativeVariantForObject. if (obj is IDispatch) { @@ -381,7 +381,7 @@ public static unsafe int IDispatchInvoke( && (flags & ComTypes.INVOKEKIND.INVOKE_FUNC) != 0 && (flags & (ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT | ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF)) == 0) { - // Re-invoke with no result argument to accomodate Word + // Re-invoke with no result argument to accommodate Word hresult = pfnIDispatchInvoke(dispatchPointer, memberDispId, &IID_NULL, 0, (ushort)ComTypes.INVOKEKIND.INVOKE_FUNC, pDispParams, null, pExcepInfo, pArgErr); } diff --git a/src/System.Management.Automation/engine/ComInterop/ComTypeClassDesc.cs b/src/System.Management.Automation/engine/ComInterop/ComTypeClassDesc.cs index fdf0751d85c..2f2886d6555 100644 --- a/src/System.Management.Automation/engine/ComInterop/ComTypeClassDesc.cs +++ b/src/System.Management.Automation/engine/ComInterop/ComTypeClassDesc.cs @@ -17,10 +17,7 @@ internal class ComTypeClassDesc : ComTypeDesc, IDynamicMetaObjectProvider public object CreateInstance() { - if (_typeObj == null) - { - _typeObj = Type.GetTypeFromCLSID(Guid); - } + _typeObj ??= Type.GetTypeFromCLSID(Guid); return Activator.CreateInstance(Type.GetTypeFromCLSID(Guid)); } @@ -46,19 +43,12 @@ private void AddInterface(ComTypes.ITypeInfo itfTypeInfo, bool isSourceItf) if (isSourceItf) { - if (_sourceItfs == null) - { - _sourceItfs = new LinkedList(); - } + _sourceItfs ??= new LinkedList(); _sourceItfs.AddLast(itfName); } else { - if (_itfs == null) - { - _itfs = new LinkedList(); - } - + _itfs ??= new LinkedList(); _itfs.AddLast(itfName); } } diff --git a/src/System.Management.Automation/engine/ComInterop/IDispatchComObject.cs b/src/System.Management.Automation/engine/ComInterop/IDispatchComObject.cs index c3a16639b62..5335a18c6de 100644 --- a/src/System.Management.Automation/engine/ComInterop/IDispatchComObject.cs +++ b/src/System.Management.Automation/engine/ComInterop/IDispatchComObject.cs @@ -271,10 +271,7 @@ internal override IList GetMemberNames(bool dataOnly) internal override IList> GetMembers(IEnumerable names) { - if (names == null) - { - names = GetMemberNames(true); - } + names ??= GetMemberNames(true); Type comType = RuntimeCallableWrapper.GetType(); diff --git a/src/System.Management.Automation/engine/ComInterop/InteropServices/ComEventsMethod.cs b/src/System.Management.Automation/engine/ComInterop/InteropServices/ComEventsMethod.cs index d31ca0299c9..f8ff1e09855 100644 --- a/src/System.Management.Automation/engine/ComInterop/InteropServices/ComEventsMethod.cs +++ b/src/System.Management.Automation/engine/ComInterop/InteropServices/ComEventsMethod.cs @@ -83,10 +83,7 @@ private void PreProcessSignature() && pi.ParameterType.HasElementType && pi.ParameterType.GetElementType()!.IsEnum) { - if (targetTypes == null) - { - targetTypes = new Type?[_expectedParamsCount]; - } + targetTypes ??= new Type?[_expectedParamsCount]; targetTypes[i] = pi.ParameterType.GetElementType(); } diff --git a/src/System.Management.Automation/engine/ComInterop/SplatCallSite.cs b/src/System.Management.Automation/engine/ComInterop/SplatCallSite.cs index ecbff91a36c..926bb460a09 100644 --- a/src/System.Management.Automation/engine/ComInterop/SplatCallSite.cs +++ b/src/System.Management.Automation/engine/ComInterop/SplatCallSite.cs @@ -30,10 +30,7 @@ internal object Invoke(object[] args) Debug.Assert(args != null); // Create a CallSite and invoke it. - if (_site == null) - { - _site = CallSite>.Create(SplatInvokeBinder.Instance); - } + _site ??= CallSite>.Create(SplatInvokeBinder.Instance); return _site.Target(_site, _callable, args); } diff --git a/src/System.Management.Automation/engine/ComInterop/TypeUtils.cs b/src/System.Management.Automation/engine/ComInterop/TypeUtils.cs index 041d7ccfd07..04217bd0bb6 100644 --- a/src/System.Management.Automation/engine/ComInterop/TypeUtils.cs +++ b/src/System.Management.Automation/engine/ComInterop/TypeUtils.cs @@ -102,11 +102,9 @@ internal static MethodInfo GetUserDefinedCoercionMethod(Type convertFrom, Type c // try lifted conversion if (nnExprType != convertFrom || nnConvType != convertToType) { - method = FindConversionOperator(eMethods, nnExprType, nnConvType, implicitOnly); - if (method == null) - { - method = FindConversionOperator(cMethods, nnExprType, nnConvType, implicitOnly); - } + method = + FindConversionOperator(eMethods, nnExprType, nnConvType, implicitOnly) ?? + FindConversionOperator(cMethods, nnExprType, nnConvType, implicitOnly); if (method != null) { return method; diff --git a/src/System.Management.Automation/engine/ComInterop/VarEnumSelector.cs b/src/System.Management.Automation/engine/ComInterop/VarEnumSelector.cs index c7f6b120919..275f75f4bb2 100644 --- a/src/System.Management.Automation/engine/ComInterop/VarEnumSelector.cs +++ b/src/System.Management.Automation/engine/ComInterop/VarEnumSelector.cs @@ -211,7 +211,7 @@ private static List GetConversionsToComPrimitiveTypeFamilies(Type argum if (TypeUtils.IsImplicitlyConvertible(argumentType, candidateManagedType, true)) { compatibleComTypes.Add(candidateType); - // Move on to the next type family. We need atmost one type from each family + // Move on to the next type family. We need at most one type from each family break; } } diff --git a/src/System.Management.Automation/engine/CommandBase.cs b/src/System.Management.Automation/engine/CommandBase.cs index 048eafd56c8..bb7aec959f6 100644 --- a/src/System.Management.Automation/engine/CommandBase.cs +++ b/src/System.Management.Automation/engine/CommandBase.cs @@ -233,6 +233,13 @@ internal virtual void DoStopProcessing() { } + /// + /// When overridden in the derived class, performs clean-up after the command execution. + /// + internal virtual void DoCleanResource() + { + } + #endregion Override /// diff --git a/src/System.Management.Automation/engine/CommandCompletion/CommandCompletion.cs b/src/System.Management.Automation/engine/CommandCompletion/CommandCompletion.cs index 0549f93e266..409cb902f77 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CommandCompletion.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CommandCompletion.cs @@ -182,10 +182,6 @@ public static CommandCompletion CompleteInput(string input, int cursorIndex, Has CheckScriptCallOnRemoteRunspace(remoteRunspace); if (remoteRunspace.GetCapabilities().Equals(Runspaces.RunspaceCapability.Default)) { - // Capability: - // NamedPipeTransport (0x2) -> If remoteMachine is Threshold or later - // SupportsDisconnect (0x1) -> If remoteMachine is Win8 or later - // Default (0x0) -> If remoteMachine is Win7 // Remoting to a Win7 machine. Use the legacy tab completion function from V1/V2 int replacementIndex; int replacementLength; diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs index 76be6418805..1c47667b05f 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs @@ -155,6 +155,14 @@ internal static AstAnalysisContext ExtractAstContext(Ast inputAst, Token[] input ast => IsCursorWithinOrJustAfterExtent(positionForAstSearch, ast.Extent), searchNestedScriptBlocks: true).ToList(); + // If the last ast is an unnamed block that starts with "param" the cursor is inside a param block. + // To avoid adding special handling to all the completers that look at the last ast, we remove it here because it's not useful for completion. + if (relatedAsts[^1].Extent.Text.StartsWith("param", StringComparison.OrdinalIgnoreCase) + && relatedAsts[^1] is NamedBlockAst namedBlock && namedBlock.Unnamed) + { + relatedAsts.RemoveAt(relatedAsts.Count - 1); + } + Diagnostics.Assert(tokenAtCursor == null || tokenBeforeCursor == null, "Only one of these tokens can be non-null"); return new AstAnalysisContext(tokenAtCursor, tokenBeforeCursor, relatedAsts, replacementIndex); @@ -175,10 +183,7 @@ private CompletionContext InitializeCompletionContext(TypeInferenceContext typeI { var astContext = ExtractAstContext(_ast, _tokens, _cursorPosition); - if (typeInferenceContext.CurrentTypeDefinitionAst == null) - { - typeInferenceContext.CurrentTypeDefinitionAst = Ast.GetAncestorTypeDefinitionAst(astContext.RelatedAsts.Last()); - } + typeInferenceContext.CurrentTypeDefinitionAst ??= Ast.GetAncestorTypeDefinitionAst(astContext.RelatedAsts.Last()); ExecutionContext executionContext = typeInferenceContext.ExecutionContext; @@ -416,7 +421,11 @@ internal List GetResultHelper(CompletionContext completionCont case TokenKind.Generic: case TokenKind.MinusMinus: // for native commands '--' case TokenKind.Identifier: - result = GetResultForIdentifier(completionContext, ref replacementIndex, ref replacementLength, isQuotedString); + if (!tokenAtCursor.TokenFlags.HasFlag(TokenFlags.TypeName)) + { + result = GetResultForIdentifier(completionContext, ref replacementIndex, ref replacementLength, isQuotedString); + } + break; case TokenKind.Parameter: @@ -466,7 +475,8 @@ internal List GetResultHelper(CompletionContext completionCont case TokenKind.QuestionDot: replacementIndex += tokenAtCursor.Text.Length; replacementLength = 0; - result = CompletionCompleters.CompleteMember(completionContext, @static: tokenAtCursor.Kind == TokenKind.ColonColon); + result = CompletionCompleters.CompleteMember(completionContext, @static: tokenAtCursor.Kind == TokenKind.ColonColon, ref replacementLength); + break; case TokenKind.Comment: @@ -490,6 +500,12 @@ internal List GetResultHelper(CompletionContext completionCont return completions; } } + else if (lastAst.Parent is IndexExpressionAst indexExpressionAst) + { + // Handles quoted string inside index expression like: $PSVersionTable[""] + completionContext.WordToComplete = (tokenAtCursor as StringToken).Value; + return CompletionCompleters.CompleteIndexExpression(completionContext, indexExpressionAst.Target); + } result = GetResultForString(completionContext, ref replacementIndex, ref replacementLength, isQuotedString); break; @@ -519,9 +535,14 @@ internal List GetResultHelper(CompletionContext completionCont break; case TokenKind.Comma: - // Handle array elements such as dir .\cd, || dir -Path: .\cd, - if (lastAst is ErrorExpressionAst && - (lastAst.Parent is CommandAst || lastAst.Parent is CommandParameterAst)) + // Handle array elements such as the followings: + // - `dir .\cd,` + // - `dir -Path: .\cd,` + // - `dir .\abc.txt, -File` + // - `dir -Path .\abc.txt, -File` + // - `dir -Path: .\abc.txt, -File` + if (lastAst is ErrorExpressionAst or ArrayLiteralAst && + lastAst.Parent is CommandAst or CommandParameterAst) { replacementIndex += replacementLength; replacementLength = 0; @@ -546,8 +567,7 @@ internal List GetResultHelper(CompletionContext completionCont // { // DependsOn=@('[user]x',|) // - bool unused; - result = GetResultForEnumPropertyValueOfDSCResource(completionContext, string.Empty, ref replacementIndex, ref replacementLength, out unused); + result = GetResultForEnumPropertyValueOfDSCResource(completionContext, string.Empty, ref replacementIndex, ref replacementLength, out _); } break; @@ -675,12 +695,75 @@ internal List GetResultHelper(CompletionContext completionCont // DependsOn=@(|) // DependsOn=(| // - bool unused; - result = GetResultForEnumPropertyValueOfDSCResource(completionContext, string.Empty, ref replacementIndex, ref replacementLength, out unused); + result = GetResultForEnumPropertyValueOfDSCResource(completionContext, string.Empty, ref replacementIndex, ref replacementLength, out _); } break; } + + case TokenKind.Format: + case TokenKind.Not: + case TokenKind.Bnot: + case TokenKind.And: + case TokenKind.Or: + case TokenKind.Xor: + case TokenKind.Band: + case TokenKind.Bor: + case TokenKind.Bxor: + case TokenKind.Join: + case TokenKind.Ieq: + case TokenKind.Ine: + case TokenKind.Ige: + case TokenKind.Igt: + case TokenKind.Ilt: + case TokenKind.Ile: + case TokenKind.Ilike: + case TokenKind.Inotlike: + case TokenKind.Imatch: + case TokenKind.Inotmatch: + case TokenKind.Ireplace: + case TokenKind.Icontains: + case TokenKind.Inotcontains: + case TokenKind.Iin: + case TokenKind.Inotin: + case TokenKind.Isplit: + case TokenKind.Ceq: + case TokenKind.Cne: + case TokenKind.Cge: + case TokenKind.Cgt: + case TokenKind.Clt: + case TokenKind.Cle: + case TokenKind.Clike: + case TokenKind.Cnotlike: + case TokenKind.Cmatch: + case TokenKind.Cnotmatch: + case TokenKind.Creplace: + case TokenKind.Ccontains: + case TokenKind.Cnotcontains: + case TokenKind.Cin: + case TokenKind.Cnotin: + case TokenKind.Csplit: + case TokenKind.Is: + case TokenKind.IsNot: + case TokenKind.As: + case TokenKind.Shl: + case TokenKind.Shr: + result = CompletionCompleters.CompleteOperator(tokenAtCursor.Text); + break; + + case TokenKind.LBracket: + if (lastAst.Parent is IndexExpressionAst indexExpression) + { + // Handles index expression with cursor right after lbracket like: $PSVersionTable[] + completionContext.WordToComplete = string.Empty; + result = CompletionCompleters.CompleteIndexExpression(completionContext, indexExpression.Target); + if (result.Count > 0) + { + replacementIndex++; + replacementLength--; + } + } + break; default: if ((tokenAtCursor.TokenFlags & TokenFlags.Keyword) != 0) { @@ -805,6 +888,43 @@ internal List GetResultHelper(CompletionContext completionCont result = GetResultForIdentifierInConfiguration(completionContext, configAst, keywordAst, out matched); } } + + // Handles following scenario where user is tab completing a member on an empty line: + // "Hello". + // + if ((result is null || result.Count == 0) && tokenBeforeCursor is not null) + { + switch (completionContext.TokenBeforeCursor.Kind) + { + + case TokenKind.Dot: + case TokenKind.ColonColon: + case TokenKind.QuestionDot: + replacementIndex = cursor.Offset; + replacementLength = 0; + result = CompletionCompleters.CompleteMember(completionContext, @static: completionContext.TokenBeforeCursor.Kind == TokenKind.ColonColon, ref replacementLength); + break; + + case TokenKind.LParen: + case TokenKind.Comma: + if (lastAst is AttributeAst) + { + result = GetResultForAttributeArgument(completionContext, ref replacementIndex, ref replacementLength); + } + + break; + case TokenKind.LBracket: + if (lastAst.Parent is IndexExpressionAst indexExpression) + { + // Handles index expression where cursor is on a new line after the lbracket like: $PSVersionTable[\n] + completionContext.WordToComplete = string.Empty; + result = CompletionCompleters.CompleteIndexExpression(completionContext, indexExpression.Target); + } + break; + default: + break; + } + } } else if (completionContext.TokenAtCursor == null) { @@ -846,10 +966,39 @@ internal List GetResultHelper(CompletionContext completionCont break; } - bool unused; - result = GetResultForEnumPropertyValueOfDSCResource(completionContext, string.Empty, ref replacementIndex, ref replacementLength, out unused); + result = GetResultForEnumPropertyValueOfDSCResource(completionContext, string.Empty, ref replacementIndex, ref replacementLength, out _); + break; + } + case TokenKind.Break: + case TokenKind.Continue: + { + if ((lastAst is BreakStatementAst breakStatement && breakStatement.Label is null) + || (lastAst is ContinueStatementAst continueStatement && continueStatement.Label is null)) + { + result = CompleteLoopLabel(completionContext); + } break; } + case TokenKind.Dot: + case TokenKind.ColonColon: + case TokenKind.QuestionDot: + // Handles following scenario with whitespace after member access token: "Hello". + replacementIndex = cursor.Offset; + replacementLength = 0; + result = CompletionCompleters.CompleteMember(completionContext, @static: tokenBeforeCursor.Kind == TokenKind.ColonColon, ref replacementLength); + if (result is not null && result.Count > 0) + { + return result; + } + break; + case TokenKind.LBracket: + if (lastAst.Parent is IndexExpressionAst indexExpression) + { + // Handles index expression with whitespace between lbracket and cursor like: $PSVersionTable[ ] + completionContext.WordToComplete = string.Empty; + result = CompletionCompleters.CompleteIndexExpression(completionContext, indexExpression.Target); + } + break; default: break; } @@ -903,6 +1052,11 @@ internal List GetResultHelper(CompletionContext completionCont typeNameToComplete = FindTypeNameToComplete(typeConstraintAst.TypeName, _cursorPosition); } } + + if (typeNameToComplete is null && tokenAtCursor?.TokenFlags.HasFlag(TokenFlags.TypeName) == true) + { + typeNameToComplete = new TypeName(tokenAtCursor.Extent, tokenAtCursor.Text); + } if (typeNameToComplete != null) { @@ -918,6 +1072,12 @@ internal List GetResultHelper(CompletionContext completionCont if (result == null || result.Count == 0) { result = GetResultForHashtable(completionContext); + // Handles the following scenario: [ipaddress]@{Address=""; } + if (result?.Count > 0) + { + replacementIndex = completionContext.CursorPosition.Offset; + replacementLength = 0; + } } if (result == null || result.Count == 0) @@ -940,59 +1100,56 @@ internal List GetResultHelper(CompletionContext completionCont // Helper method to auto complete hashtable key private static List GetResultForHashtable(CompletionContext completionContext) { - var lastAst = completionContext.RelatedAsts.Last(); - HashtableAst tempHashtableAst = null; - IScriptPosition cursor = completionContext.CursorPosition; - var hashTableAst = lastAst as HashtableAst; - if (hashTableAst != null) + Ast lastRelatedAst = null; + var cursorPosition = completionContext.CursorPosition; + + // Enumeration is used over the LastAst pattern because empty lines following a key-value pair will set LastAst to the value. + // Example: + // @{ + // Key1="Value1" + // + // } + // In this case the last 3 Asts will be StringConstantExpression, CommandExpression, and Pipeline instead of the expected Hashtable + for (int i = completionContext.RelatedAsts.Count - 1; i >= 0; i--) + { + Ast ast = completionContext.RelatedAsts[i]; + if (cursorPosition.Offset >= ast.Extent.StartOffset && cursorPosition.Offset <= ast.Extent.EndOffset) + { + lastRelatedAst = ast; + break; + } + } + + if (lastRelatedAst is HashtableAst hashtableAst) { - // Check if the cursor within the hashtable - if (cursor.Offset < hashTableAst.Extent.EndOffset) + // Cursor is just after the hashtable: @{} + if (completionContext.TokenAtCursor is not null && completionContext.TokenAtCursor.Kind == TokenKind.RCurly) { - tempHashtableAst = hashTableAst; + return null; } - else if (cursor.Offset == hashTableAst.Extent.EndOffset) + + bool cursorIsWithinOrOnSameLineAsKeypair = false; + foreach (var pair in hashtableAst.KeyValuePairs) { - // Exclude the scenario that cursor at the end of hashtable, i.e. after '}' - if (completionContext.TokenAtCursor == null || - completionContext.TokenAtCursor.Kind != TokenKind.RCurly) + if (cursorPosition.Offset >= pair.Item1.Extent.StartOffset + && (cursorPosition.Offset <= pair.Item2.Extent.EndOffset || cursorPosition.LineNumber == pair.Item2.Extent.EndLineNumber)) { - tempHashtableAst = hashTableAst; + cursorIsWithinOrOnSameLineAsKeypair = true; + break; } } - } - else - { - // Handle property completion on a blank line for DynamicKeyword statement - Ast lastChildofHashtableAst; - hashTableAst = Ast.GetAncestorHashtableAst(lastAst, out lastChildofHashtableAst); - // Check if the hashtable within a DynamicKeyword statement - if (hashTableAst != null) + if (cursorIsWithinOrOnSameLineAsKeypair) { - var keywordAst = Ast.GetAncestorAst(hashTableAst); - if (keywordAst != null) + var tokenBeforeOrAtCursor = completionContext.TokenBeforeCursor ?? completionContext.TokenAtCursor; + if (tokenBeforeOrAtCursor.Kind != TokenKind.Semi) { - // Handle only empty line - if (string.IsNullOrWhiteSpace(cursor.Line)) - { - // Check if the cursor outside of last child of hashtable and within the hashtable - if (cursor.Offset > lastChildofHashtableAst.Extent.EndOffset && - cursor.Offset <= hashTableAst.Extent.EndOffset) - { - tempHashtableAst = hashTableAst; - } - } + return null; } } - } - - hashTableAst = tempHashtableAst; - if (hashTableAst != null) - { completionContext.ReplacementIndex = completionContext.CursorPosition.Offset; completionContext.ReplacementLength = 0; - return CompletionCompleters.CompleteHashtableKey(completionContext, hashTableAst); + return CompletionCompleters.CompleteHashtableKey(completionContext, hashtableAst); } return null; @@ -1734,10 +1891,7 @@ private static List GetResultForIdentifierInConfiguration( usageString = Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.GetDSCResourceUsageString(keyword); } - if (results == null) - { - results = new List(); - } + results ??= new List(); results.Add(new CompletionResult( keyword.Keyword, @@ -1750,7 +1904,7 @@ private static List GetResultForIdentifierInConfiguration( return results; } - private List GetResultForIdentifier(CompletionContext completionContext, ref int replacementIndex, ref int replacementLength, bool isQuotedString) + private static List GetResultForIdentifier(CompletionContext completionContext, ref int replacementIndex, ref int replacementLength, bool isQuotedString) { var tokenAtCursor = completionContext.TokenAtCursor; var lastAst = completionContext.RelatedAsts.Last(); @@ -1759,6 +1913,11 @@ private List GetResultForIdentifier(CompletionContext completi var tokenAtCursorText = tokenAtCursor.Text; completionContext.WordToComplete = tokenAtCursorText; + if (lastAst.Parent is BreakStatementAst || lastAst.Parent is ContinueStatementAst) + { + return CompleteLoopLabel(completionContext); + } + var strConst = lastAst as StringConstantExpressionAst; if (strConst != null) { @@ -1813,70 +1972,82 @@ private List GetResultForIdentifier(CompletionContext completi } } } - - result = GetResultForAttributeArgument(completionContext, ref replacementIndex, ref replacementLength); - if (result != null) return result; + if (completionContext.TokenAtCursor.TokenFlags == TokenFlags.MemberName) + { + result = GetResultForAttributeArgument(completionContext, ref replacementIndex, ref replacementLength); + if (result is not null) + { + return result; + } + } if ((tokenAtCursor.TokenFlags & TokenFlags.CommandName) != 0) { // Handle completion for a path with variable, such as: $PSHOME\ty if (completionContext.RelatedAsts.Count > 0 && completionContext.RelatedAsts[0] is ScriptBlockAst) { - Ast cursorAst = null; - var cursorPosition = (InternalScriptPosition)_cursorPosition; - int offsetBeforeCmdName = cursorPosition.Offset - tokenAtCursorText.Length; - if (offsetBeforeCmdName >= 0) - { - var cursorBeforeCmdName = cursorPosition.CloneWithNewOffset(offsetBeforeCmdName); - var scriptBlockAst = (ScriptBlockAst)completionContext.RelatedAsts[0]; - cursorAst = GetLastAstAtCursor(scriptBlockAst, cursorBeforeCmdName); - } + Ast cursorAst = completionContext.RelatedAsts[0].FindAll( + ast => ast.Extent.EndOffset <= tokenAtCursor.Extent.StartOffset + && ast.Extent is not EmptyScriptExtent, + searchNestedScriptBlocks: true).LastOrDefault(); - if (cursorAst != null && - cursorAst.Extent.EndLineNumber == tokenAtCursor.Extent.StartLineNumber && - cursorAst.Extent.EndColumnNumber == tokenAtCursor.Extent.StartColumnNumber) + if (cursorAst is not null) { - if (tokenAtCursorText.IndexOfAny(Utils.Separators.Directory) == 0) + if (cursorAst.Extent.EndOffset == tokenAtCursor.Extent.StartOffset) { - string wordToComplete = - CompletionCompleters.ConcatenateStringPathArguments(cursorAst as CommandElementAst, tokenAtCursorText, completionContext); - if (wordToComplete != null) + if (tokenAtCursorText.IndexOfAny(Utils.Separators.Directory) == 0) { - completionContext.WordToComplete = wordToComplete; - result = new List(CompletionCompleters.CompleteFilename(completionContext)); - if (result.Count > 0) + string wordToComplete = + CompletionCompleters.ConcatenateStringPathArguments(cursorAst as CommandElementAst, tokenAtCursorText, completionContext); + if (wordToComplete != null) { + completionContext.WordToComplete = wordToComplete; + result = new List(CompletionCompleters.CompleteFilename(completionContext)); + if (result.Count > 0) + { + replacementIndex = cursorAst.Extent.StartScriptPosition.Offset; + replacementLength += cursorAst.Extent.Text.Length; + } + + return result; + } + else + { + var variableAst = cursorAst as VariableExpressionAst; + string fullPath = variableAst != null + ? CompletionCompleters.CombineVariableWithPartialPath( + variableAst: variableAst, + extraText: tokenAtCursorText, + executionContext: completionContext.ExecutionContext) + : null; + + if (fullPath == null) { return result; } + + // Continue trying the filename/commandname completion for scenarios like this: $aa\d + completionContext.WordToComplete = fullPath; replacementIndex = cursorAst.Extent.StartScriptPosition.Offset; replacementLength += cursorAst.Extent.Text.Length; - } - return result; + completionContext.ReplacementIndex = replacementIndex; + completionContext.ReplacementLength = replacementLength; + } } - else + // Continue trying the filename/commandname completion for scenarios like this: $aa[get- + else if (cursorAst is not ErrorExpressionAst || cursorAst.Parent is not IndexExpressionAst) { - var variableAst = cursorAst as VariableExpressionAst; - string fullPath = variableAst != null - ? CompletionCompleters.CombineVariableWithPartialPath( - variableAst: variableAst, - extraText: tokenAtCursorText, - executionContext: completionContext.ExecutionContext) - : null; - - if (fullPath == null) { return result; } - - // Continue trying the filename/commandname completion for scenarios like this: $aa\d - completionContext.WordToComplete = fullPath; - replacementIndex = cursorAst.Extent.StartScriptPosition.Offset; - replacementLength += cursorAst.Extent.Text.Length; - - completionContext.ReplacementIndex = replacementIndex; - completionContext.ReplacementLength = replacementLength; + return result; } } - // Continue trying the filename/commandname completion for scenarios like this: $aa[get- - else if (cursorAst is not ErrorExpressionAst || cursorAst.Parent is not IndexExpressionAst) + + if (cursorAst.Parent is IndexExpressionAst indexExpression && indexExpression.Index is ErrorExpressionAst) { - return result; + if (completionContext.WordToComplete.EndsWith(']')) + { + completionContext.WordToComplete = completionContext.WordToComplete.Remove(completionContext.WordToComplete.Length - 1); + } + + // Handles index expression with unquoted word like: $PSVersionTable[psver] + return CompletionCompleters.CompleteIndexExpression(completionContext, indexExpression.Target); } } } @@ -1966,8 +2137,24 @@ private List GetResultForIdentifier(CompletionContext completi } TokenKind memberOperator = TokenKind.Unknown; - bool isMemberCompletion = (lastAst.Parent is MemberExpressionAst); - bool isStatic = isMemberCompletion && ((MemberExpressionAst)lastAst.Parent).Static; + bool isMemberCompletion = lastAst.Parent is MemberExpressionAst; + bool isStatic = false; + if (isMemberCompletion) + { + var currentExpression = (MemberExpressionAst)lastAst.Parent; + // Handles following scenario with an incomplete member access token at the end of the statement: + // [System.IO.FileInfo]::new().Directory.BaseName.Length. + // Traverses up the expressions until it finds one under at the cursor + while (currentExpression.Extent.EndOffset >= completionContext.CursorPosition.Offset + && currentExpression.Expression is MemberExpressionAst memberExpression + && memberExpression.Member.Extent.EndOffset >= completionContext.CursorPosition.Offset) + { + currentExpression = memberExpression; + } + + isStatic = currentExpression.Static; + } + bool isWildcard = false; if (!isMemberCompletion) @@ -2018,7 +2205,7 @@ private List GetResultForIdentifier(CompletionContext completi if (isMemberCompletion) { - result = CompletionCompleters.CompleteMember(completionContext, @static: (isStatic || memberOperator == TokenKind.ColonColon)); + result = CompletionCompleters.CompleteMember(completionContext, @static: (isStatic || memberOperator == TokenKind.ColonColon), ref replacementLength); // If the last token was just a '.', we tried to complete members. That may // have failed because it wasn't really an attempt to complete a member, in @@ -2102,6 +2289,7 @@ private List GetResultForIdentifier(CompletionContext completi result = CompletionCompleters.CompleteCommandArgument(completionContext); replacementIndex = completionContext.ReplacementIndex; replacementLength = completionContext.ReplacementLength; + return result; } @@ -2111,10 +2299,11 @@ private static List GetResultForAttributeArgument(CompletionCo Type attributeType = null; string argName = string.Empty; Ast argAst = completionContext.RelatedAsts.Find(static ast => ast is NamedAttributeArgumentAst); - NamedAttributeArgumentAst namedArgAst = argAst as NamedAttributeArgumentAst; - if (argAst != null && namedArgAst != null) + AttributeAst attAst; + if (argAst is NamedAttributeArgumentAst namedArgAst) { - attributeType = ((AttributeAst)namedArgAst.Parent).TypeName.GetReflectionAttributeType(); + attAst = (AttributeAst)namedArgAst.Parent; + attributeType = attAst.TypeName.GetReflectionAttributeType(); argName = namedArgAst.ArgumentName; replacementIndex = namedArgAst.Extent.StartOffset; replacementLength = argName.Length; @@ -2122,21 +2311,34 @@ private static List GetResultForAttributeArgument(CompletionCo else { Ast astAtt = completionContext.RelatedAsts.Find(static ast => ast is AttributeAst); - AttributeAst attAst = astAtt as AttributeAst; - if (astAtt != null && attAst != null) + attAst = astAtt as AttributeAst; + if (attAst is not null) { attributeType = attAst.TypeName.GetReflectionAttributeType(); } } - if (attributeType != null) + if (attributeType is not null) { + int cursorPosition = completionContext.CursorPosition.Offset; + var existingArguments = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var namedArgument in attAst.NamedArguments) + { + if (cursorPosition < namedArgument.Extent.StartOffset || cursorPosition > namedArgument.Extent.EndOffset) + { + existingArguments.Add(namedArgument.ArgumentName); + } + } + PropertyInfo[] propertyInfos = attributeType.GetProperties(BindingFlags.Public | BindingFlags.Instance); List result = new List(); foreach (PropertyInfo property in propertyInfos) { - // Ignore getter-only properties, including 'TypeId' (all attributes inherit it). - if (!property.CanWrite) { continue; } + // Ignore getter-only properties and properties that have already been set. + if (!property.CanWrite || existingArguments.Contains(property.Name)) + { + continue; + } if (property.Name.StartsWith(argName, StringComparison.OrdinalIgnoreCase)) { @@ -2200,5 +2402,40 @@ private static List CompleteFileNameAsCommand(CompletionContex return result; } + + /// + /// Complete loop labels after labeled control flow statements such as Break and Continue. + /// + private static List CompleteLoopLabel(CompletionContext completionContext) + { + var result = new List(); + foreach (Ast ast in completionContext.RelatedAsts) + { + if (ast is LabeledStatementAst labeledStatement + && labeledStatement.Label is not null + && (completionContext.WordToComplete is null || labeledStatement.Label.StartsWith(completionContext.WordToComplete, StringComparison.OrdinalIgnoreCase))) + { + result.Add(new CompletionResult(labeledStatement.Label, labeledStatement.Label, CompletionResultType.Text, labeledStatement.Extent.Text)); + } + else if (ast is ErrorStatementAst errorStatement) + { + // Handles incomplete do/switch loops (other labeled statements do not need this special treatment) + // The regex looks for the loopLabel of errorstatements that look like do/switch loops + // For example in ":Label do " it will find "Label". + var labelMatch = Regex.Match(errorStatement.Extent.Text, @"(?<=^:)\w+(?=\s+(do|switch)\b(?!-))", RegexOptions.IgnoreCase); + if (labelMatch.Success) + { + result.Add(new CompletionResult(labelMatch.Value, labelMatch.Value, CompletionResultType.Text, errorStatement.Extent.Text)); + } + } + } + + if (result.Count == 0) + { + return null; + } + + return result; + } } } diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index d8360e714d8..2ddb472f2dd 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -333,10 +333,7 @@ internal static List MakeCommandsUnique(IEnumerable var commandList = keyValuePair.Value as List; if (commandList != null) { - if (endResults == null) - { - endResults = new List(); - } + endResults ??= new List(); // The first command might be an un-prefixed commandInfo that we get by importing a module with the -Prefix parameter, // in that case, we should add the module name qualification because if the module is not in the module path, calling @@ -495,8 +492,7 @@ internal static List CompleteCommandParameter(CompletionContex DynamicKeywordStatementAst keywordAst = null; for (int i = context.RelatedAsts.Count - 1; i >= 0; i--) { - if (keywordAst == null) - keywordAst = context.RelatedAsts[i] as DynamicKeywordStatementAst; + keywordAst ??= context.RelatedAsts[i] as DynamicKeywordStatementAst; parameterAst = (context.RelatedAsts[i] as CommandParameterAst); if (parameterAst != null) break; } @@ -533,6 +529,7 @@ internal static List CompleteCommandParameter(CompletionContex return result; } + bool bindPositionalParameters = true; if (parameterAst != null) { // Parent must be a command @@ -551,10 +548,24 @@ internal static List CompleteCommandParameter(CompletionContex // Parent must be a command commandAst = (CommandAst)dashAst.Parent; partialName = string.Empty; + + // If the user tries to tab complete a new parameter in front of a positional argument like: dir - C:\ + // the user may want to add the parameter name so we don't want to bind positional arguments + if (commandAst is not null) + { + foreach (var element in commandAst.CommandElements) + { + if (element.Extent.StartOffset > context.TokenAtCursor.Extent.StartOffset) + { + bindPositionalParameters = element is CommandParameterAst; + break; + } + } + } } PseudoBindingInfo pseudoBinding = new PseudoParameterBinder() - .DoPseudoParameterBinding(commandAst, null, parameterAst, PseudoParameterBinder.BindingType.ParameterCompletion); + .DoPseudoParameterBinding(commandAst, null, parameterAst, PseudoParameterBinder.BindingType.ParameterCompletion, bindPositionalParameters); // The command cannot be found or it's not a cmdlet, not a script cmdlet, not a function. // Try completing as if it the parameter is a command argument for native command completion. if (pseudoBinding == null) @@ -1329,7 +1340,8 @@ internal static List CompleteCommandArgument(CompletionContext context.Options.Remove("LiteralPaths"); } - if (context.WordToComplete != string.Empty && context.WordToComplete.Contains('-')) + // The word to complete contains a dash and it's not the first character. We try command names in this case. + if (context.WordToComplete.IndexOf('-') > 0) { var commandResults = CompleteCommand(context); if (commandResults != null) @@ -1608,8 +1620,7 @@ private static void CompletePositionalArgument( } else { - if (positionalParam == null) - positionalParam = param; + positionalParam ??= param; } } else @@ -1657,9 +1668,9 @@ private static void CompletePositionalArgument( /// /// /// If the argument completion falls into these pre-defined cases: - /// 1. The matching parameter is of type Enum - /// 2. The matching parameter is of type SwitchParameter - /// 3. The matching parameter is declared with ValidateSetAttribute + /// 1. The matching parameter is declared with ValidateSetAttribute + /// 2. The matching parameter is of type Enum + /// 3. The matching parameter is of type SwitchParameter /// 4. Falls into the native command argument completion /// a null instance of CompletionResult is added to the end of the /// "result" list, to indicate that this particular argument completion @@ -1682,70 +1693,13 @@ private static void ProcessParameter( parameterType = parameterType.GetElementType(); } - if (parameterType.IsEnum) - { - RemoveLastNullCompletionResult(result); - - string enumString = LanguagePrimitives.EnumSingleTypeConverter.EnumValues(parameterType); - string separator = CultureInfo.CurrentUICulture.TextInfo.ListSeparator; - string[] enumArray = enumString.Split(separator, StringSplitOptions.RemoveEmptyEntries); - - string wordToComplete = context.WordToComplete; - string quote = HandleDoubleAndSingleQuote(ref wordToComplete); - - var pattern = WildcardPattern.Get(wordToComplete + "*", WildcardOptions.IgnoreCase); - var enumList = new List(); - - foreach (string value in enumArray) - { - if (wordToComplete.Equals(value, StringComparison.OrdinalIgnoreCase)) - { - string completionText = quote == string.Empty ? value : quote + value + quote; - fullMatch = new CompletionResult(completionText, value, CompletionResultType.ParameterValue, value); - continue; - } - - if (pattern.IsMatch(value)) - { - enumList.Add(value); - } - } - - if (fullMatch != null) - { - result.Add(fullMatch); - } - - enumList.Sort(); - result.AddRange(from entry in enumList - let completionText = quote == string.Empty ? entry : quote + entry + quote - select new CompletionResult(completionText, entry, CompletionResultType.ParameterValue, entry)); - - result.Add(CompletionResult.Null); - return; - } - - if (parameterType.Equals(typeof(SwitchParameter))) - { - RemoveLastNullCompletionResult(result); - - if (context.WordToComplete == string.Empty || context.WordToComplete.Equals("$", StringComparison.Ordinal)) - { - result.Add(new CompletionResult("$true", "$true", CompletionResultType.ParameterValue, "$true")); - result.Add(new CompletionResult("$false", "$false", CompletionResultType.ParameterValue, "$false")); - } - - result.Add(CompletionResult.Null); - return; - } - foreach (ValidateArgumentsAttribute att in parameter.Parameter.ValidationAttributes) { if (att is ValidateSetAttribute setAtt) { RemoveLastNullCompletionResult(result); - string wordToComplete = context.WordToComplete; + string wordToComplete = context.WordToComplete ?? string.Empty; string quote = HandleDoubleAndSingleQuote(ref wordToComplete); var pattern = WildcardPattern.Get(wordToComplete + "*", WildcardOptions.IgnoreCase); @@ -1753,7 +1707,10 @@ private static void ProcessParameter( foreach (string value in setAtt.ValidValues) { - if (value == string.Empty) { continue; } + if (value == string.Empty) + { + continue; + } if (wordToComplete.Equals(value, StringComparison.OrdinalIgnoreCase)) { @@ -1804,6 +1761,63 @@ private static void ProcessParameter( } } + if (parameterType.IsEnum) + { + RemoveLastNullCompletionResult(result); + + string enumString = LanguagePrimitives.EnumSingleTypeConverter.EnumValues(parameterType); + string separator = CultureInfo.CurrentUICulture.TextInfo.ListSeparator; + string[] enumArray = enumString.Split(separator, StringSplitOptions.RemoveEmptyEntries); + + string wordToComplete = context.WordToComplete ?? string.Empty; + string quote = HandleDoubleAndSingleQuote(ref wordToComplete); + + var pattern = WildcardPattern.Get(wordToComplete + "*", WildcardOptions.IgnoreCase); + var enumList = new List(); + + foreach (string value in enumArray) + { + if (wordToComplete.Equals(value, StringComparison.OrdinalIgnoreCase)) + { + string completionText = quote == string.Empty ? value : quote + value + quote; + fullMatch = new CompletionResult(completionText, value, CompletionResultType.ParameterValue, value); + continue; + } + + if (pattern.IsMatch(value)) + { + enumList.Add(value); + } + } + + if (fullMatch != null) + { + result.Add(fullMatch); + } + + enumList.Sort(); + result.AddRange(from entry in enumList + let completionText = quote == string.Empty ? entry : quote + entry + quote + select new CompletionResult(completionText, entry, CompletionResultType.ParameterValue, entry)); + + result.Add(CompletionResult.Null); + return; + } + + if (parameterType.Equals(typeof(SwitchParameter))) + { + RemoveLastNullCompletionResult(result); + + if (context.WordToComplete == string.Empty || context.WordToComplete.Equals("$", StringComparison.Ordinal)) + { + result.Add(new CompletionResult("$true", "$true", CompletionResultType.ParameterValue, "$true")); + result.Add(new CompletionResult("$false", "$false", CompletionResultType.ParameterValue, "$false")); + } + + result.Add(CompletionResult.Null); + return; + } + NativeCommandArgumentCompletion(commandName, parameter.Parameter, result, commandAst, context, boundArguments); } @@ -2274,7 +2288,7 @@ private static void NativeCommandArgumentCompletion( { if (parameterName.Equals("MemberName", StringComparison.OrdinalIgnoreCase)) { - NativeCompletionMemberName(context, result, commandAst); + NativeCompletionMemberName(context, result, commandAst, boundArguments?[parameterName], propertiesOnly: false); } break; @@ -2286,7 +2300,7 @@ private static void NativeCommandArgumentCompletion( { if (parameterName.Equals("Property", StringComparison.OrdinalIgnoreCase)) { - NativeCompletionMemberName(context, result, commandAst); + NativeCompletionMemberName(context, result, commandAst, boundArguments?[parameterName]); } break; @@ -2298,7 +2312,7 @@ private static void NativeCommandArgumentCompletion( { if (parameterName.Equals("Property", StringComparison.OrdinalIgnoreCase)) { - NativeCompletionMemberName(context, result, commandAst); + NativeCompletionMemberName(context, result, commandAst, boundArguments?[parameterName]); } else if (parameterName.Equals("View", StringComparison.OrdinalIgnoreCase)) { @@ -2313,7 +2327,7 @@ private static void NativeCommandArgumentCompletion( || parameterName.Equals("ExcludeProperty", StringComparison.OrdinalIgnoreCase) || parameterName.Equals("ExpandProperty", StringComparison.OrdinalIgnoreCase)) { - NativeCompletionMemberName(context, result, commandAst); + NativeCompletionMemberName(context, result, commandAst, boundArguments?[parameterName]); } break; @@ -2335,8 +2349,22 @@ private static void NativeCommandArgumentCompletion( case "Invoke-CimMethod": case "New-CimInstance": case "Register-CimIndicationEvent": + case "Set-CimInstance": { - NativeCompletionCimCommands(parameterName, boundArguments, result, commandAst, context); + // Avoids completion for parameters that expect a hashtable. + if (parameterName.Equals("Arguments", StringComparison.OrdinalIgnoreCase) + || (parameterName.Equals("Property", StringComparison.OrdinalIgnoreCase) && !commandName.Equals("Get-CimInstance"))) + { + break; + } + + HashSet excludedValues = null; + if (parameterName.Equals("Property", StringComparison.OrdinalIgnoreCase) && boundArguments["Property"] is AstPair pair) + { + excludedValues = GetParameterValues(pair, context.CursorPosition.Offset); + } + + NativeCompletionCimCommands(parameterName, boundArguments, result, commandAst, context, excludedValues, commandName); break; } @@ -2507,7 +2535,9 @@ private static void NativeCompletionCimCommands( Dictionary boundArguments, List result, CommandAst commandAst, - CompletionContext context) + CompletionContext context, + HashSet excludedValues, + string commandName) { if (boundArguments != null) { @@ -2528,6 +2558,7 @@ private static void NativeCompletionCimCommands( } } + RemoveLastNullCompletionResult(result); if (parameter.Equals("Namespace", StringComparison.OrdinalIgnoreCase)) { NativeCompletionCimNamespace(result, context); @@ -2573,6 +2604,16 @@ private static void NativeCompletionCimCommands( { NativeCompletionCimMethodName(pseudoboundCimNamespace, pseudoboundClassName, !gotInstance, result, context); } + else if (parameter.Equals("Arguments", StringComparison.OrdinalIgnoreCase)) + { + string pseudoboundMethodName = NativeCommandArgumentCompletion_ExtractSecondaryArgument(boundArguments, "MethodName").FirstOrDefault(); + NativeCompletionCimMethodArgumentName(pseudoboundCimNamespace, pseudoboundClassName, pseudoboundMethodName, excludedValues, result, context); + } + else if (parameter.Equals("Property", StringComparison.OrdinalIgnoreCase)) + { + bool includeReadOnly = !commandName.Equals("Set-CimInstance", StringComparison.OrdinalIgnoreCase); + NativeCompletionCimPropertyName(pseudoboundCimNamespace, pseudoboundClassName, includeReadOnly, excludedValues, result, context); + } } } @@ -2715,6 +2756,82 @@ private static void NativeCompletionCimMethodName( result.AddRange(localResults.OrderBy(static x => x.ListItemText, StringComparer.OrdinalIgnoreCase)); } + private static void NativeCompletionCimMethodArgumentName( + string pseudoboundNamespace, + string pseudoboundClassName, + string pseudoboundMethodName, + HashSet excludedParameters, + List result, + CompletionContext context) + { + if (string.IsNullOrWhiteSpace(pseudoboundClassName) || string.IsNullOrWhiteSpace(pseudoboundMethodName)) + { + return; + } + + CimClass cimClass; + using (var cimSession = CimSession.Create(null)) + { + using var options = new CimOperationOptions(); + options.Flags |= CimOperationFlags.LocalizedQualifiers; + cimClass = cimSession.GetClass(pseudoboundNamespace ?? "root/cimv2", pseudoboundClassName, options); + } + + var methodParameters = cimClass.CimClassMethods[pseudoboundMethodName]?.Parameters; + if (methodParameters is null) + { + return; + } + + foreach (var parameter in methodParameters) + { + if ((string.IsNullOrEmpty(context.WordToComplete) || parameter.Name.StartsWith(context.WordToComplete, StringComparison.OrdinalIgnoreCase)) + && (excludedParameters is null || !excludedParameters.Contains(parameter.Name)) + && parameter.Qualifiers["In"]?.Value is true) + { + string parameterDescription = parameter.Qualifiers["Description"]?.Value as string ?? string.Empty; + string toolTip = $"[{CimInstanceAdapter.CimTypeToTypeNameDisplayString(parameter.CimType)}] {parameterDescription}"; + result.Add(new CompletionResult(parameter.Name, parameter.Name, CompletionResultType.Property, toolTip)); + } + } + } + + private static void NativeCompletionCimPropertyName( + string pseudoboundNamespace, + string pseudoboundClassName, + bool includeReadOnly, + HashSet excludedProperties, + List result, + CompletionContext context) + { + if (string.IsNullOrWhiteSpace(pseudoboundClassName)) + { + return; + } + + CimClass cimClass; + using (var cimSession = CimSession.Create(null)) + { + using var options = new CimOperationOptions(); + options.Flags |= CimOperationFlags.LocalizedQualifiers; + cimClass = cimSession.GetClass(pseudoboundNamespace ?? "root/cimv2", pseudoboundClassName, options); + } + + foreach (var property in cimClass.CimClassProperties) + { + bool isReadOnly = (property.Flags & CimFlags.ReadOnly) != 0; + if ((!isReadOnly || (isReadOnly && includeReadOnly)) + && (string.IsNullOrEmpty(context.WordToComplete) || property.Name.StartsWith(context.WordToComplete, StringComparison.OrdinalIgnoreCase)) + && (excludedProperties is null || !excludedProperties.Contains(property.Name))) + { + string propertyDescription = property.Qualifiers["Description"]?.Value as string ?? string.Empty; + string accessString = isReadOnly ? "{ get; }" : "{ get; set; }"; + string toolTip = $"[{CimInstanceAdapter.CimTypeToTypeNameDisplayString(property.CimType)}] {accessString} {propertyDescription}"; + result.Add(new CompletionResult(property.Name, property.Name, CompletionResultType.Property, toolTip)); + } + } + } + private static readonly ConcurrentDictionary> s_cimNamespaceToClassNames = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); @@ -3844,17 +3961,39 @@ private static IEnumerable GetInferenceTypes(CompletionContext conte return prevType; } - private static void NativeCompletionMemberName(CompletionContext context, List result, CommandAst commandAst) + private static void NativeCompletionMemberName(CompletionContext context, List result, CommandAst commandAst, AstParameterArgumentPair parameterInfo, bool propertiesOnly = true) { IEnumerable prevType = GetInferenceTypes(context, commandAst); if (prevType is not null) { - CompleteMemberByInferredType(context.TypeInferenceContext, prevType, result, context.WordToComplete + "*", filter: IsPropertyMember, isStatic: false); + HashSet excludedMembers = null; + if (parameterInfo is AstPair pair) + { + excludedMembers = GetParameterValues(pair, context.CursorPosition.Offset); + } + + Func filter = propertiesOnly ? IsPropertyMember : null; + CompleteMemberByInferredType(context.TypeInferenceContext, prevType, result, context.WordToComplete + "*", filter, isStatic: false, excludedMembers); } result.Add(CompletionResult.Null); } + /// + /// Returns all string values bound to a parameter except the one the cursor is currently at. + /// + private static HashSetGetParameterValues(AstPair parameter, int cursorOffset) + { + var result = new HashSet(StringComparer.OrdinalIgnoreCase); + var parameterValues = parameter.Argument.FindAll(ast => !(cursorOffset >= ast.Extent.StartOffset && cursorOffset <= ast.Extent.EndOffset) && ast is StringConstantExpressionAst, searchNestedScriptBlocks: false); + foreach (Ast ast in parameterValues) + { + result.Add(ast.Extent.Text); + } + + return result; + } + private static void NativeCompletionFormatViewName( CompletionContext context, Dictionary boundArguments, @@ -3997,9 +4136,11 @@ private static ArgumentLocation FindTargetArgumentLocation(Collection token.Extent.StartOffset) + if ((token.Kind == TokenKind.Parameter && token.Extent.StartOffset == arg.Parameter.Extent.StartOffset) + || (token.Extent.StartOffset > arg.Argument.Extent.StartOffset && token.Extent.EndOffset < arg.Argument.Extent.EndOffset)) { - // case: Get-Cmdlet -Param abc + // case 1: Get-Cmdlet -Param abc + // case 2: dir -Path .\abc.txt, -File return new ArgumentLocation() { Argument = arg, IsPositional = false, Position = -1 }; } } @@ -4173,7 +4314,8 @@ internal static IEnumerable CompleteFilename(CompletionContext var results = new List(); // First, try to match \\server\share - var shareMatch = Regex.Match(wordToComplete, "^\\\\\\\\([^\\\\]+)\\\\([^\\\\]*)$"); + // support both / and \ when entering UNC paths for typing convenience (#17111) + var shareMatch = Regex.Match(wordToComplete, @"^(?:\\\\|//)([^\\/]+)(?:\\|/)([^\\/]*)$"); if (shareMatch.Success) { // Only match share names, no filenames. @@ -4642,6 +4784,7 @@ internal static List CompleteVariable(CompletionContext contex var lastAst = context.RelatedAsts?.Last(); var variableAst = lastAst as VariableExpressionAst; var prefix = variableAst != null && variableAst.Splatted ? "@" : "$"; + bool tokenAtCursorUsedBraces = context.TokenAtCursor is not null && context.TokenAtCursor.Text.StartsWith("${"); // Look for variables in the input (e.g. parameters, etc.) before checking session state - these // variables might not exist in session state yet. @@ -4787,7 +4930,7 @@ internal static List CompleteVariable(CompletionContext contex } } - var completedName = (name.IndexOfAny(s_charactersRequiringQuotes) == -1) + var completedName = (!tokenAtCursorUsedBraces && name.IndexOfAny(s_charactersRequiringQuotes) == -1) ? prefix + provider + name : prefix + "{" + provider + name + "}"; AddUniqueVariable(hashedResults, results, completedName, name, tooltip); @@ -4810,7 +4953,7 @@ internal static List CompleteVariable(CompletionContext contex if (!string.IsNullOrEmpty(name)) { name = "env:" + name; - var completedName = (name.IndexOfAny(s_charactersRequiringQuotes) == -1) + var completedName = (!tokenAtCursorUsedBraces && name.IndexOfAny(s_charactersRequiringQuotes) == -1) ? prefix + name : prefix + "{" + name + "}"; AddUniqueVariable(hashedResults, results, completedName, name, "[string]" + name); @@ -4825,7 +4968,7 @@ internal static List CompleteVariable(CompletionContext contex { if (wildcardPattern.IsMatch(specialVariable)) { - var completedName = (specialVariable.IndexOfAny(s_charactersRequiringQuotes) == -1) + var completedName = (!tokenAtCursorUsedBraces && specialVariable.IndexOfAny(s_charactersRequiringQuotes) == -1) ? prefix + specialVariable : prefix + "{" + specialVariable + "}"; @@ -4851,7 +4994,7 @@ internal static List CompleteVariable(CompletionContext contex var name = driveInfo.Name; if (name != null && !string.IsNullOrWhiteSpace(name) && name.Length > 1) { - var completedName = (name.IndexOfAny(s_charactersRequiringQuotes) == -1) + var completedName = (!tokenAtCursorUsedBraces && name.IndexOfAny(s_charactersRequiringQuotes) == -1) ? prefix + name + ":" : prefix + "{" + name + ":}"; @@ -4867,7 +5010,7 @@ internal static List CompleteVariable(CompletionContext contex { if (scopePattern.IsMatch(scope)) { - var completedName = (scope.IndexOfAny(s_charactersRequiringQuotes) == -1) + var completedName = (!tokenAtCursorUsedBraces && scope.IndexOfAny(s_charactersRequiringQuotes) == -1) ? prefix + scope : prefix + "{" + scope + "}"; AddUniqueVariable(hashedResults, results, completedName, scope, scope); @@ -5398,7 +5541,7 @@ private static FunctionDefinitionAst GetCommentHelpFunctionTarget(CompletionCont } Ast lastAst = context.RelatedAsts[^1]; - Ast firstAstAfterComment = lastAst.Find(ast => ast.Extent.StartOffset >= context.TokenAtCursor.Extent.EndOffset, searchNestedScriptBlocks: false); + Ast firstAstAfterComment = lastAst.Find(ast => ast.Extent.StartOffset >= context.TokenAtCursor.Extent.EndOffset && ast is not NamedBlockAst, searchNestedScriptBlocks: false); // Comment-based help can apply to a following function definition if it starts within 2 lines int commentEndLine = context.TokenAtCursor.Extent.EndLineNumber + 2; @@ -5425,9 +5568,7 @@ private static FunctionDefinitionAst GetCommentHelpFunctionTarget(CompletionCont // Helpblock before function if (firstAstAfterComment is not null && firstAstAfterComment.Extent.StartLineNumber <= commentEndLine - && firstAstAfterComment is NamedBlockAst block - && block.Statements.Count > 0 - && block.Statements[0] is FunctionDefinitionAst statement) + && firstAstAfterComment is FunctionDefinitionAst statement) { return statement; } @@ -5506,10 +5647,10 @@ private static List CompleteCommentParameterValue(CompletionCo private static readonly HashSet s_dscCollectionVariables = new HashSet(StringComparer.OrdinalIgnoreCase) { "SelectedNodes", "AllNodes" }; - internal static List CompleteMember(CompletionContext context, bool @static) + internal static List CompleteMember(CompletionContext context, bool @static, ref int replacementLength) { // If we get here, we know that either: - // * the cursor appeared immediately after a member access token ('.' or '::'). + // * the cursor appeared after a member access token ('.' or '::'). // * the parent of the ast on the cursor was a member expression. // // In the first case, we have 2 possibilities: @@ -5517,31 +5658,35 @@ internal static List CompleteMember(CompletionContext context, // * the last ast is a string constant, with something like: echo $foo. var results = new List(); - var lastAst = context.RelatedAsts.Last(); - var lastAstAsMemberExpr = lastAst as MemberExpressionAst; + var memberName = "*"; Ast memberNameCandidateAst = null; ExpressionAst targetExpr = null; - if (lastAstAsMemberExpr != null) + + if (lastAst is MemberExpressionAst LastAstAsMemberExpression) { // If the cursor is not inside the member name in the member expression, assume // that the user had incomplete input, but the parser got lucky and succeeded parsing anyway. - if (context.TokenAtCursor.Extent.StartOffset >= lastAstAsMemberExpr.Member.Extent.StartOffset) + if (context.TokenAtCursor is not null && context.TokenAtCursor.Extent.StartOffset >= LastAstAsMemberExpression.Member.Extent.StartOffset) { - memberNameCandidateAst = lastAstAsMemberExpr.Member; + memberNameCandidateAst = LastAstAsMemberExpression.Member; } - targetExpr = lastAstAsMemberExpr.Expression; + targetExpr = LastAstAsMemberExpression.Expression; + // Handles scenario where the cursor is after the member access token but before the text + // like: "".Le + // which completes the member using the partial text after the cursor. + if (LastAstAsMemberExpression.Member is StringConstantExpressionAst stringExpression && stringExpression.Extent.StartOffset <= context.CursorPosition.Offset) + { + memberName = $"{stringExpression.Value}*"; + } } else { memberNameCandidateAst = lastAst; } - var memberNameAst = memberNameCandidateAst as StringConstantExpressionAst; - - var memberName = "*"; - if (memberNameAst != null) + if (memberNameCandidateAst is StringConstantExpressionAst memberNameAst) { // Make sure to correctly handle: echo $foo. if (!memberNameAst.Value.Equals(".", StringComparison.OrdinalIgnoreCase) && !memberNameAst.Value.Equals("::", StringComparison.OrdinalIgnoreCase)) @@ -5555,8 +5700,7 @@ internal static List CompleteMember(CompletionContext context, return results; } - var commandAst = lastAst.Parent as CommandAst; - if (commandAst != null) + if (lastAst.Parent is CommandAst commandAst) { int i; for (i = commandAst.CommandElements.Count - 1; i >= 0; --i) @@ -5576,10 +5720,36 @@ internal static List CompleteMember(CompletionContext context, targetExpr = nextToLastAst as ExpressionAst; } } - else if (lastAst.Parent is MemberExpressionAst) + else if (lastAst.Parent is MemberExpressionAst parentAsMemberExpression) { + if (lastAst is ErrorExpressionAst) + { + // Handles scenarios like $PSVersionTable.PSVersi.Major. + // where the cursor is moved back to a previous member expression while + // there's an incomplete member expression at the end + targetExpr = parentAsMemberExpression; + do + { + if (targetExpr is MemberExpressionAst memberExpression) + { + targetExpr = memberExpression.Expression; + } + else + { + break; + } + } while (targetExpr.Extent.EndOffset >= context.CursorPosition.Offset); + + if (targetExpr.Parent != parentAsMemberExpression + && targetExpr.Parent is MemberExpressionAst memberAst + && memberAst.Member is StringConstantExpressionAst stringExpression + && stringExpression.Extent.StartOffset <= context.CursorPosition.Offset) + { + memberName = $"{stringExpression.Value}*"; + } + } // If 'targetExpr' has already been set, we should skip this step. This is for some member completion - // cases in ISE. In ISE, we may add a new statement in the middle of existing statements as follows: + // cases in VSCode, where we may add a new statement in the middle of existing statements as follows: // $xml = New-Object Xml // $xml. // $xml.Save("C:\data.xml") @@ -5587,24 +5757,45 @@ internal static List CompleteMember(CompletionContext context, // a MemberExpressionAst '$xml.$xml', whose parent is still a MemberExpressionAst '$xml.$xml.Save'. // But here we DO NOT want to re-assign 'targetExpr' to be '$xml.$xml'. 'targetExpr' in this case // should be '$xml'. - if (targetExpr == null) + else { - var memberExprAst = (MemberExpressionAst)lastAst.Parent; - targetExpr = memberExprAst.Expression; + targetExpr ??= parentAsMemberExpression.Expression; } } - else if (lastAst.Parent is BinaryExpressionAst && context.TokenAtCursor.Kind.Equals(TokenKind.Multiply)) + else if (lastAst.Parent is BinaryExpressionAst binaryExpression && context.TokenAtCursor.Kind.Equals(TokenKind.Multiply)) { - var memberExprAst = ((BinaryExpressionAst)lastAst.Parent).Left as MemberExpressionAst; - if (memberExprAst != null) + if (binaryExpression.Left is MemberExpressionAst memberExpression) { - targetExpr = memberExprAst.Expression; - if (memberExprAst.Member is StringConstantExpressionAst) + targetExpr = memberExpression.Expression; + if (memberExpression.Member is StringConstantExpressionAst stringExpression) { - memberName = ((StringConstantExpressionAst)memberExprAst.Member).Value + "*"; + memberName = $"{stringExpression.Value}*"; } } } + else if (lastAst.Parent is ErrorStatementAst errorStatement) + { + // Handles switches like: + // switch ($x) + // { + // 'RandomString'. + // { } + // } + Ast astBeforeMemberAccessToken = null; + for (int i = errorStatement.Bodies.Count - 1; i >= 0; i--) + { + astBeforeMemberAccessToken = errorStatement.Bodies[i]; + if (astBeforeMemberAccessToken.Extent.EndOffset < lastAst.Extent.EndOffset) + { + break; + } + } + + if (astBeforeMemberAccessToken is ExpressionAst expression) + { + targetExpr = expression; + } + } if (targetExpr == null) { @@ -5637,6 +5828,11 @@ internal static List CompleteMember(CompletionContext context, inferredTypes = AstTypeInference.InferTypeOf(targetExpr, context.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval).ToArray(); } + if (!@static && inferredTypes.Length == 1 && inferredTypes[0].Name.Equals("System.Void", StringComparison.OrdinalIgnoreCase)) + { + return results; + } + if (inferredTypes != null && inferredTypes.Length > 0) { // Use inferred types if we have any @@ -5693,6 +5889,12 @@ internal static List CompleteMember(CompletionContext context, } } + if (memberName != "*" && results.Count > 0) + { + // -1 because membername always has a trailing wildcard * + replacementLength = memberName.Length - 1; + } + return results; } @@ -5744,6 +5946,42 @@ private static bool IsInDscContext(ExpressionAst expression) return Ast.GetAncestorAst(expression) != null; } + internal static List CompleteIndexExpression(CompletionContext context, ExpressionAst indexTarget) + { + var result = new List(); + object value; + if (SafeExprEvaluator.TrySafeEval(indexTarget, context.ExecutionContext, out value) + && value is not null + && PSObject.Base(value) is IDictionary dictionary) + { + foreach (var key in dictionary.Keys) + { + if (key is string keyAsString && keyAsString.StartsWith(context.WordToComplete, StringComparison.OrdinalIgnoreCase)) + { + result.Add(new CompletionResult($"'{keyAsString}'", keyAsString, CompletionResultType.Property, keyAsString)); + } + } + } + else + { + var inferredTypes = AstTypeInference.InferTypeOf(indexTarget, context.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval); + foreach (var type in inferredTypes) + { + if (type is PSSyntheticTypeName synthetic) + { + foreach (var member in synthetic.Members) + { + if (member.Name.StartsWith(context.WordToComplete, StringComparison.OrdinalIgnoreCase)) + { + result.Add(new CompletionResult($"'{member.Name}'", member.Name, CompletionResultType.Property, member.Name)); + } + } + } + } + } + return result; + } + private static void CompleteFormatViewByInferredType(CompletionContext context, string[] inferredTypeNames, List results, string commandName) { var typeInfoDB = context.TypeInferenceContext.ExecutionContext.FormatDBManager.GetTypeInfoDataBase(); @@ -5797,7 +6035,7 @@ private static void CompleteFormatViewByInferredType(CompletionContext context, } } - internal static void CompleteMemberByInferredType(TypeInferenceContext context, IEnumerable inferredTypes, List results, string memberName, Func filter, bool isStatic) + internal static void CompleteMemberByInferredType(TypeInferenceContext context, IEnumerable inferredTypes, List results, string memberName, Func filter, bool isStatic, HashSet excludedMembers = null) { bool extensionMethodsAdded = false; HashSet typeNameUsed = new HashSet(StringComparer.OrdinalIgnoreCase); @@ -5813,7 +6051,7 @@ internal static void CompleteMemberByInferredType(TypeInferenceContext context, var members = context.GetMembersByInferredType(psTypeName, isStatic, filter); foreach (var member in members) { - AddInferredMember(member, memberNamePattern, results); + AddInferredMember(member, memberNamePattern, results, excludedMembers); } // Check if we need to complete against the extension methods 'Where' and 'ForEach' @@ -5833,14 +6071,13 @@ internal static void CompleteMemberByInferredType(TypeInferenceContext context, .AddCommandWithPreferenceSetting("Microsoft.PowerShell.Utility\\Sort-Object") .AddParameter("Property", new[] { "ResultType", "ListItemText" }) .AddParameter("Unique"); - Exception unused; - var sortedResults = powerShellExecutionHelper.ExecuteCurrentPowerShell(out unused, results); + var sortedResults = powerShellExecutionHelper.ExecuteCurrentPowerShell(out _, results); results.Clear(); results.AddRange(sortedResults.Select(static psobj => PSObject.Base(psobj) as CompletionResult)); } } - private static void AddInferredMember(object member, WildcardPattern memberNamePattern, List results) + private static void AddInferredMember(object member, WildcardPattern memberNamePattern, List results, HashSet excludedMembers) { string memberName = null; bool isMethod = false; @@ -5885,21 +6122,45 @@ private static void AddInferredMember(object member, WildcardPattern memberNameP getToolTip = () => GetCimPropertyToString(cimProperty); } - var memberAst = member as MemberAst; - if (memberAst != null) + if (member is MemberAst memberAst) { - memberName = memberAst is CompilerGeneratedMemberFunctionAst ? "new" : memberAst.Name; - isMethod = memberAst is FunctionMemberAst || memberAst is CompilerGeneratedMemberFunctionAst; + if (memberAst is CompilerGeneratedMemberFunctionAst) + { + memberName = "new"; + isMethod = true; + } + else if (memberAst is FunctionMemberAst functionMember) + { + memberName = functionMember.IsConstructor ? "new" : functionMember.Name; + isMethod = true; + } + else + { + memberName = memberAst.Name; + isMethod = false; + } getToolTip = memberAst.GetTooltip; } - if (memberName == null || !memberNamePattern.IsMatch(memberName)) + if (memberName == null || !memberNamePattern.IsMatch(memberName) || (excludedMembers is not null && excludedMembers.Contains(memberName))) { return; } var completionResultType = isMethod ? CompletionResultType.Method : CompletionResultType.Property; - var completionText = isMethod ? memberName + "(" : memberName; + string completionText; + if (isMethod) + { + completionText = $"{memberName}("; + } + else if (memberName.IndexOfAny(s_charactersRequiringQuotes) != -1) + { + completionText = $"'{memberName}'"; + } + else + { + completionText = memberName; + } results.Add(new CompletionResult(completionText, memberName, completionResultType, getToolTip())); } @@ -6814,13 +7075,25 @@ internal static List CompleteHashtableKeyForDynamicKeyword( internal static List CompleteHashtableKey(CompletionContext completionContext, HashtableAst hashtableAst) { + int cursorOffset = completionContext.CursorPosition.Offset; + string wordToComplete = completionContext.WordToComplete; + var excludedKeys = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var keyPair in hashtableAst.KeyValuePairs) + { + // Exclude all existing keys, except the key the cursor is currently at + if (!(cursorOffset >= keyPair.Item1.Extent.StartOffset && cursorOffset <= keyPair.Item1.Extent.EndOffset)) + { + excludedKeys.Add(keyPair.Item1.Extent.Text); + } + } + var typeAst = hashtableAst.Parent as ConvertExpressionAst; if (typeAst != null) { var result = new List(); CompleteMemberByInferredType( completionContext.TypeInferenceContext, AstTypeInference.InferTypeOf(typeAst, completionContext.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval), - result, completionContext.WordToComplete + "*", IsWriteablePropertyMember, isStatic: false); + result, wordToComplete + "*", IsWriteablePropertyMember, isStatic: false, excludedKeys); return result; } @@ -6920,7 +7193,7 @@ internal static List CompleteHashtableKey(CompletionContext co case "Format-List": case "Format-Wide": case "Format-Custom": - return GetSpecialHashTableKeyMembers("Expression", "FormatString", "Label"); + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "FormatString", "Label"); } return null; @@ -6935,37 +7208,128 @@ internal static List CompleteHashtableKey(CompletionContext co var result = new List(); CompleteMemberByInferredType( completionContext.TypeInferenceContext, inferredType, - result, completionContext.WordToComplete + "*", IsWriteablePropertyMember, isStatic: false); + result, completionContext.WordToComplete + "*", IsWriteablePropertyMember, isStatic: false, excludedKeys); return result; case "Select-Object": - return GetSpecialHashTableKeyMembers("Name", "Expression"); + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Name", "Expression"); case "Sort-Object": - return GetSpecialHashTableKeyMembers("Expression", "Ascending", "Descending"); + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "Ascending", "Descending"); case "Group-Object": - return GetSpecialHashTableKeyMembers("Expression"); + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression"); case "Format-Table": - return GetSpecialHashTableKeyMembers("Expression", "FormatString", "Label", "Width", "Alignment"); + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "FormatString", "Label", "Width", "Alignment"); case "Format-List": - return GetSpecialHashTableKeyMembers("Expression", "FormatString", "Label"); + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "FormatString", "Label"); case "Format-Wide": - return GetSpecialHashTableKeyMembers("Expression", "FormatString"); + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "FormatString"); case "Format-Custom": - return GetSpecialHashTableKeyMembers("Expression", "Depth"); + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "Depth"); + case "Set-CimInstance": + case "New-CimInstance": + var results = new List(); + NativeCompletionCimCommands(parameterName, binding.BoundArguments, results, commandAst, completionContext, excludedKeys, binding.CommandName); + // this method adds a null CompletionResult to the list but we don't want that here. + if (results.Count > 1) + { + results.RemoveAt(results.Count - 1); + return results; + } + + return null; + } + } + + if (parameterName.Equals("FilterHashtable", StringComparison.OrdinalIgnoreCase)) + { + switch (binding.CommandName) + { + case "Get-WinEvent": + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "LogName", "ProviderName", "Path", "Keywords", "ID", "Level", + "StartTime", "EndTime", "UserID", "Data", "SuppressHashFilter"); + } + } + + if (parameterName.Equals("Arguments", StringComparison.OrdinalIgnoreCase)) + { + switch (binding.CommandName) + { + case "Invoke-CimMethod": + var result = new List(); + NativeCompletionCimCommands(parameterName, binding.BoundArguments, result, commandAst, completionContext, excludedKeys, binding.CommandName); + // this method adds a null CompletionResult to the list but we don't want that here. + if (result.Count > 1) + { + result.RemoveAt(result.Count - 1); + return result; + } + + return null; + } + } + } + } + + if (ast.Parent is AssignmentStatementAst assignment && assignment.Left is VariableExpressionAst assignmentVar) + { + var firstSplatUse = completionContext.RelatedAsts[0].Find( + currentAst => + currentAst.Extent.StartOffset > hashtableAst.Extent.EndOffset + && currentAst is VariableExpressionAst splatVar + && splatVar.Splatted + && splatVar.VariablePath.UserPath.Equals(assignmentVar.VariablePath.UserPath, StringComparison.OrdinalIgnoreCase), + searchNestedScriptBlocks: true) as VariableExpressionAst; + + if (firstSplatUse is not null && firstSplatUse.Parent is CommandAst command) + { + var binding = new PseudoParameterBinder() + .DoPseudoParameterBinding( + command, + pipeArgumentType: null, + paramAstAtCursor: null, + PseudoParameterBinder.BindingType.ParameterCompletion); + + if (binding is null) + { + return null; + } + + var results = new List(); + foreach (var parameter in binding.UnboundParameters) + { + if (!excludedKeys.Contains(parameter.Parameter.Name) + && (wordToComplete is null || parameter.Parameter.Name.StartsWith(wordToComplete, StringComparison.OrdinalIgnoreCase))) + { + results.Add(new CompletionResult(parameter.Parameter.Name, parameter.Parameter.Name, CompletionResultType.ParameterName, $"[{parameter.Parameter.Type.Name}]")); } } + + if (results.Count > 0) + { + return results; + } } } return null; } - private static List GetSpecialHashTableKeyMembers(params string[] keys) + private static List GetSpecialHashTableKeyMembers(HashSet excludedKeys, string wordToComplete, params string[] keys) { - // Resources were removed because they missed the deadline for loc. - // return keys.Select(key => new CompletionResult(key, key, CompletionResultType.Property, - // ResourceManagerCache.GetResourceString(typeof(CompletionCompleters).Assembly, - // "TabCompletionStrings", key + "HashKeyDescription"))).ToList(); - return keys.Select(static key => new CompletionResult(key, key, CompletionResultType.Property, key)).ToList(); + var result = new List(); + foreach (string key in keys) + { + if ((string.IsNullOrEmpty(wordToComplete) || key.StartsWith(wordToComplete, StringComparison.OrdinalIgnoreCase)) && !excludedKeys.Contains(key)) + { + result.Add(new CompletionResult(key, key, CompletionResultType.Property, key)); + } + } + + if (result.Count == 0) + { + return null; + } + + return result; } #endregion Hashtable Keys @@ -7010,32 +7374,40 @@ internal static bool IsPathSafelyExpandable(ExpandableStringExpressionAst expand internal static string CombineVariableWithPartialPath(VariableExpressionAst variableAst, string extraText, ExecutionContext executionContext) { var varPath = variableAst.VariablePath; - if (varPath.IsVariable || varPath.DriveName.Equals("env", StringComparison.OrdinalIgnoreCase)) + if (!varPath.IsVariable && !varPath.DriveName.Equals("env", StringComparison.OrdinalIgnoreCase)) { - try - { - // We check the strict mode inside GetVariableValue - object value = VariableOps.GetVariableValue(varPath, executionContext, variableAst); - var strValue = (value == null) ? string.Empty : value as string; + return null; + } - if (strValue == null) - { - object baseObj = PSObject.Base(value); - if (baseObj is string || baseObj.GetType().IsPrimitive) - { - strValue = LanguagePrimitives.ConvertTo(value); - } - } + if (varPath.UnqualifiedPath.Equals(SpecialVariables.PSScriptRoot, StringComparison.OrdinalIgnoreCase) + && !string.IsNullOrEmpty(variableAst.Extent.File)) + { + return Path.GetDirectoryName(variableAst.Extent.File) + extraText; + } - if (strValue != null) + try + { + // We check the strict mode inside GetVariableValue + object value = VariableOps.GetVariableValue(varPath, executionContext, variableAst); + var strValue = (value == null) ? string.Empty : value as string; + + if (strValue == null) + { + object baseObj = PSObject.Base(value); + if (baseObj is string || baseObj?.GetType()?.IsPrimitive is true) { - return strValue + extraText; + strValue = LanguagePrimitives.ConvertTo(value); } } - catch (Exception) + + if (strValue != null) { + return strValue + extraText; } } + catch (Exception) + { + } return null; } diff --git a/src/System.Management.Automation/engine/CommandCompletion/PseudoParameterBinder.cs b/src/System.Management.Automation/engine/CommandCompletion/PseudoParameterBinder.cs index c98be4ccac8..97b39cf39f5 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/PseudoParameterBinder.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/PseudoParameterBinder.cs @@ -949,8 +949,9 @@ internal enum BindingType /// Indicate the type of the piped-in argument. /// The CommandParameterAst the cursor is pointing at. /// Indicates whether pseudo binding is for argument binding, argument completion, or parameter completion. + /// Indicates if the pseudo binding should bind positional parameters /// PseudoBindingInfo. - internal PseudoBindingInfo DoPseudoParameterBinding(CommandAst command, Type pipeArgumentType, CommandParameterAst paramAstAtCursor, BindingType bindingType) + internal PseudoBindingInfo DoPseudoParameterBinding(CommandAst command, Type pipeArgumentType, CommandParameterAst paramAstAtCursor, BindingType bindingType, bool bindPositional = true) { if (command == null) { @@ -1008,12 +1009,15 @@ internal PseudoBindingInfo DoPseudoParameterBinding(CommandAst command, Type pip unboundArguments = BindNamedParameters(); _bindingEffective = _currentParameterSetFlag != 0; - // positional binding - unboundArguments = BindPositionalParameter( - unboundArguments, - _currentParameterSetFlag, - _defaultParameterSetFlag, - bindingType); + if (bindPositional) + { + // positional binding + unboundArguments = BindPositionalParameter( + unboundArguments, + _currentParameterSetFlag, + _defaultParameterSetFlag, + bindingType); + } // VFRA/pipeline binding if the given command is a binary cmdlet or a script cmdlet if (!_function) @@ -1218,10 +1222,7 @@ private bool PrepareCommandElements(ExecutionContext context) var parameter = _commandElements[commandIndex] as CommandParameterAst; if (parameter != null) { - if (argumentsToGetDynamicParameters != null) - { - argumentsToGetDynamicParameters.Add(parameter.Extent.Text); - } + argumentsToGetDynamicParameters?.Add(parameter.Extent.Text); AstPair parameterArg = parameter.Argument != null ? new AstPair(parameter) @@ -1231,21 +1232,32 @@ private bool PrepareCommandElements(ExecutionContext context) } else { - var dash = _commandElements[commandIndex] as StringConstantExpressionAst; - if (dash != null && dash.Value.Trim().Equals("-", StringComparison.OrdinalIgnoreCase)) + object valueToAdd; + ExpressionAst expressionToAdd; + if (_commandElements[commandIndex] is ConstantExpressionAst constant) { - // "-" is represented by StringConstantExpressionAst. Most likely the user type a tab here, - // and we don't want it be treated as an argument - continue; - } + if (constant.Extent.Text.Equals("-", StringComparison.Ordinal)) + { + // A value of "-" is most likely the user trying to tab here, + // and we don't want it be treated as an argument + continue; + } - var expressionArgument = _commandElements[commandIndex] as ExpressionAst; - if (expressionArgument != null) + valueToAdd = constant.Value; + expressionToAdd = constant; + } + else if (_commandElements[commandIndex] is ExpressionAst expression) { - argumentsToGetDynamicParameters?.Add(expressionArgument.Extent.Text); - - _arguments.Add(new AstPair(null, expressionArgument)); + valueToAdd = expression.Extent.Text; + expressionToAdd = expression; } + else + { + continue; + } + + argumentsToGetDynamicParameters?.Add(valueToAdd); + _arguments.Add(new AstPair(null, expressionToAdd)); } } } diff --git a/src/System.Management.Automation/engine/CommandDiscovery.cs b/src/System.Management.Automation/engine/CommandDiscovery.cs index 6d7fb77eba8..e9de849a4cc 100644 --- a/src/System.Management.Automation/engine/CommandDiscovery.cs +++ b/src/System.Management.Automation/engine/CommandDiscovery.cs @@ -376,22 +376,16 @@ private static void VerifyRequiredSnapins(IEnumerable req foreach (var requiresPSSnapIn in requiresPSSnapIns) { - IEnumerable loadedPSSnapIns = null; - loadedPSSnapIns = context.InitialSessionState.GetPSSnapIn(requiresPSSnapIn.Name); - if (loadedPSSnapIns == null || !loadedPSSnapIns.Any()) + var loadedPSSnapIn = context.InitialSessionState.GetPSSnapIn(requiresPSSnapIn.Name); + if (loadedPSSnapIn is null) { - if (requiresMissingPSSnapIns == null) - { - requiresMissingPSSnapIns = new Collection(); - } - + requiresMissingPSSnapIns ??= new Collection(); requiresMissingPSSnapIns.Add(BuildPSSnapInDisplayName(requiresPSSnapIn)); } else { // the requires PSSnapin is loaded. now check the PSSnapin version - PSSnapInInfo loadedPSSnapIn = loadedPSSnapIns.First(); - Diagnostics.Assert(loadedPSSnapIn.Version != null, + Dbg.Assert(loadedPSSnapIn.Version != null, string.Format( CultureInfo.InvariantCulture, "Version is null for loaded PSSnapin {0}.", loadedPSSnapIn)); @@ -400,11 +394,7 @@ private static void VerifyRequiredSnapins(IEnumerable req if (!AreInstalledRequiresVersionsCompatible( requiresPSSnapIn.Version, loadedPSSnapIn.Version)) { - if (requiresMissingPSSnapIns == null) - { - requiresMissingPSSnapIns = new Collection(); - } - + requiresMissingPSSnapIns ??= new Collection(); requiresMissingPSSnapIns.Add(BuildPSSnapInDisplayName(requiresPSSnapIn)); } } @@ -819,10 +809,7 @@ internal static CommandInfo LookupCommandInfo( } // Otherwise, invoke the CommandNotFound handler - if (result == null) - { - result = InvokeCommandNotFoundHandler(commandName, context, originalCommandName, commandOrigin); - } + result ??= InvokeCommandNotFoundHandler(commandName, context, originalCommandName, commandOrigin); } while (false); } else @@ -1063,14 +1050,12 @@ private static CommandInfo TryModuleAutoDiscovery(string commandName, return null; CmdletInfo cmdletInfo = context.SessionState.InvokeCommand.GetCmdlet("Microsoft.PowerShell.Core\\Get-Module"); - if ((commandOrigin == CommandOrigin.Internal) || - ((cmdletInfo != null) && (cmdletInfo.Visibility == SessionStateEntryVisibility.Public))) + if (commandOrigin == CommandOrigin.Internal || cmdletInfo?.Visibility == SessionStateEntryVisibility.Public) { // Search for a module with a matching command, as long as the user would have the ability to // import the module. cmdletInfo = context.SessionState.InvokeCommand.GetCmdlet("Microsoft.PowerShell.Core\\Import-Module"); - if (((commandOrigin == CommandOrigin.Internal) || - ((cmdletInfo != null) && (cmdletInfo.Visibility == SessionStateEntryVisibility.Public)))) + if (commandOrigin == CommandOrigin.Internal || cmdletInfo?.Visibility == SessionStateEntryVisibility.Public) { discoveryTracer.WriteLine("Executing non module-qualified search: {0}", commandName); context.CommandDiscovery.RegisterLookupCommandInfoAction("ActiveModuleSearch", commandName); @@ -1085,30 +1070,33 @@ private static CommandInfo TryModuleAutoDiscovery(string commandName, { // WinBlue:69141 - We need to get the full path here because the module path might be C:\Users\User1\DOCUME~1 // While the exportedCommands are cached, they are cached with the full path - string expandedModulePath = IO.Path.GetFullPath(modulePath); - string moduleShortName = System.IO.Path.GetFileNameWithoutExtension(expandedModulePath); + string expandedModulePath = Path.GetFullPath(modulePath); + string moduleShortName = Path.GetFileNameWithoutExtension(expandedModulePath); var exportedCommands = AnalysisCache.GetExportedCommands(expandedModulePath, false, context); if (exportedCommands == null) { continue; } - CommandTypes exportedCommandTypes; // Skip if module only has class or other types and no commands. - if (exportedCommands.TryGetValue(commandName, out exportedCommandTypes)) + if (exportedCommands.TryGetValue(commandName, out CommandTypes exportedCommandTypes)) { - Exception exception; discoveryTracer.WriteLine("Found in module: {0}", expandedModulePath); - Collection matchingModule = AutoloadSpecifiedModule(expandedModulePath, context, + Collection matchingModule = AutoloadSpecifiedModule( + expandedModulePath, + context, cmdletInfo != null ? cmdletInfo.Visibility : SessionStateEntryVisibility.Private, - out exception); - lastError = exception; - if ((matchingModule == null) || (matchingModule.Count == 0)) + out lastError); + + if (matchingModule is null || matchingModule.Count == 0) { - string error = StringUtil.Format(DiscoveryExceptions.CouldNotAutoImportMatchingModule, commandName, moduleShortName); - CommandNotFoundException commandNotFound = new CommandNotFoundException( + string errorMessage = lastError is null + ? StringUtil.Format(DiscoveryExceptions.CouldNotAutoImportMatchingModule, commandName, moduleShortName) + : StringUtil.Format(DiscoveryExceptions.CouldNotAutoImportMatchingModuleWithErrorMessage, commandName, moduleShortName, lastError.Message); + + throw new CommandNotFoundException( originalCommandName, lastError, - "CouldNotAutoloadMatchingModule", error); - throw commandNotFound; + "CouldNotAutoloadMatchingModule", + errorMessage); } result = LookupCommandInfo(commandName, commandTypes, searchResolutionOptions, commandOrigin, context); @@ -1482,7 +1470,7 @@ internal IEnumerator GetCmdletInfo(string cmdletName, bool searchAll } // The engine cmdlets get imported (via Import-Module) once when PowerShell starts and the cmdletInfo is added to PSSnapinHelpers._cmdletcache(static) with ModuleName // as "System.Management.Automation.dll" instead of the actual snapin name. The next time we load something in an InitialSessionState, we look at this _cmdletcache and - // if the the assembly is already loaded, we just return the cmdlets back. So, the CmdletInfo has moduleName has "System.Management.Automation.dll". So, when M3P Activity + // if the assembly is already loaded, we just return the cmdlets back. So, the CmdletInfo has moduleName has "System.Management.Automation.dll". So, when M3P Activity // tries to access Microsoft.PowerShell.Core\\Get-Command, it cannot. So, adding an additional check to return the correct cmdletInfo for cmdlets from core modules. else if (InitialSessionState.IsEngineModule(cmdletInfo.ModuleName)) { diff --git a/src/System.Management.Automation/engine/CommandInfo.cs b/src/System.Management.Automation/engine/CommandInfo.cs index 7e28fece99d..eb2e7163469 100644 --- a/src/System.Management.Automation/engine/CommandInfo.cs +++ b/src/System.Management.Automation/engine/CommandInfo.cs @@ -467,11 +467,8 @@ private MergedCommandParameterMetadata GetMergedCommandParameterMetadataSafely() processInCurrentThread: true, waitForCompletionInCurrentThread: true); - if (eventArgs.Exception != null) - { - // An exception happened on a different thread, rethrow it here on the correct thread. - eventArgs.Exception.Throw(); - } + // An exception happened on a different thread, rethrow it here on the correct thread. + eventArgs.Exception?.Throw(); return eventArgs.Result; } @@ -529,7 +526,7 @@ private void GetMergedCommandParameterMetadata(out MergedCommandParameterMetadat processor = scriptCommand != null ? new CommandProcessor(scriptCommand, _context, useLocalScope: true, fromScriptFile: false, sessionState: scriptCommand.ScriptBlock.SessionStateInternal ?? Context.EngineSessionState) - : new CommandProcessor((CmdletInfo)this, _context) { UseLocalScope = true }; + : new CommandProcessor((CmdletInfo)this, _context); ParameterBinderController.AddArgumentsToCommandProcessor(processor, Arguments); CommandProcessorBase oldCurrentCommandProcessor = Context.CurrentCommandProcessor; diff --git a/src/System.Management.Automation/engine/CommandMetadata.cs b/src/System.Management.Automation/engine/CommandMetadata.cs index eb59f9140a2..a9094f54b36 100644 --- a/src/System.Management.Automation/engine/CommandMetadata.cs +++ b/src/System.Management.Automation/engine/CommandMetadata.cs @@ -875,8 +875,11 @@ internal string GetProxyCommand(string helpComment, bool generateDynamicParamete end {{{5}}} + +clean +{{{6}}} <# -{6} +{7} #> ", GetDecl(), @@ -885,6 +888,7 @@ internal string GetProxyCommand(string helpComment, bool generateDynamicParamete GetBeginBlock(), GetProcessBlock(), GetEndBlock(), + GetCleanBlock(), CodeGeneration.EscapeBlockCommentContent(helpComment)); return result; @@ -1063,6 +1067,11 @@ internal string GetBeginBlock() internal string GetProcessBlock() { + // The reason we wrap scripts in 'try { } catch { throw }' (here and elsewhere) is to turn + // an exception that could be thrown from .NET method invocation into a terminating error + // that can be propagated up. + // By default, an exception thrown from .NET method is not terminating, but when enclosed + // in try/catch, it will be turned into a terminating error. return @" try { $steppablePipeline.Process($_) @@ -1113,6 +1122,18 @@ internal string GetEndBlock() "; } + internal string GetCleanBlock() + { + // Here we don't need to enclose the script in a 'try/catch' like elsewhere, because + // 1. the 'Clean' block doesn't propagate up any exception (terminating error); + // 2. only one expression in the script, so nothing else needs to be stopped when invoking the method fails. + return @" + if ($null -ne $steppablePipeline) { + $steppablePipeline.Clean() + } +"; + } + #endregion #region Helper methods for restricting commands needed by implicit and interactive remoting diff --git a/src/System.Management.Automation/engine/CommandParameter.cs b/src/System.Management.Automation/engine/CommandParameter.cs index 227795cf1d6..1c58bb87e29 100644 --- a/src/System.Management.Automation/engine/CommandParameter.cs +++ b/src/System.Management.Automation/engine/CommandParameter.cs @@ -124,10 +124,7 @@ internal bool ArgumentToBeSplatted /// internal void SetArgumentValue(Ast ast, object value) { - if (_argument == null) - { - _argument = new Argument(); - } + _argument ??= new Argument(); _argument.value = value; _argument.ast = ast; diff --git a/src/System.Management.Automation/engine/CommandPathSearch.cs b/src/System.Management.Automation/engine/CommandPathSearch.cs index a761aa638cb..a5b8a232b23 100644 --- a/src/System.Management.Automation/engine/CommandPathSearch.cs +++ b/src/System.Management.Automation/engine/CommandPathSearch.cs @@ -110,7 +110,17 @@ private void ResolveCurrentDirectoryInLookupPaths() sessionState.CurrentDrive.Provider.NameEquals(fileSystemProviderName) && sessionState.IsProviderLoaded(fileSystemProviderName); - string environmentCurrentDirectory = Directory.GetCurrentDirectory(); + string? environmentCurrentDirectory = null; + + try + { + environmentCurrentDirectory = Directory.GetCurrentDirectory(); + } + catch (FileNotFoundException) + { + // This can happen if the current working directory is deleted by another process on non-Windows + // In this case, we'll just ignore it and continue on with the current directory as null + } LocationGlobber pathResolver = _context.LocationGlobber; @@ -493,8 +503,7 @@ private void GetNewDirectoryResults(string pattern, string directory) if (name.Equals(baseNames[i], StringComparison.OrdinalIgnoreCase) || (!Platform.IsWindows && Platform.NonWindowsIsExecutable(name))) { - if (result == null) - result = new Collection(); + result ??= new Collection(); result.Add(fileNames[i]); break; } @@ -520,8 +529,7 @@ private void GetNewDirectoryResults(string pattern, string directory) if (fileName.EndsWith(allowedExt, StringComparison.OrdinalIgnoreCase) || (!Platform.IsWindows && Platform.NonWindowsIsExecutable(fileName))) { - if (result == null) - result = new Collection(); + result ??= new Collection(); result.Add(fileName); } } diff --git a/src/System.Management.Automation/engine/CommandProcessor.cs b/src/System.Management.Automation/engine/CommandProcessor.cs index fb1fe81fc54..62eebe5d937 100644 --- a/src/System.Management.Automation/engine/CommandProcessor.cs +++ b/src/System.Management.Automation/engine/CommandProcessor.cs @@ -309,13 +309,11 @@ internal override void DoBegin() internal override void ProcessRecord() { // Invoke the Command method with the request object - if (!this.RanBeginAlready) { RanBeginAlready = true; try { - // NOTICE-2004/06/08-JonN 959638 using (commandRuntime.AllowThisCommandToWrite(true)) { if (Context._debuggingMode > 0 && Command is not PSScriptCmdlet) @@ -326,12 +324,9 @@ internal override void ProcessRecord() Command.DoBeginProcessing(); } } - // 2004/03/18-JonN This is understood to be - // an FXCOP violation, cleared by KCwalina. - catch (Exception e) // Catch-all OK, 3rd party callout. + catch (Exception e) { - // This cmdlet threw an exception, so - // wrap it and bubble it up. + // This cmdlet threw an exception, so wrap it and bubble it up. throw ManageInvocationException(e); } } @@ -366,6 +361,7 @@ internal override void ProcessRecord() // NOTICE-2004/06/08-JonN 959638 using (commandRuntime.AllowThisCommandToWrite(true)) + using (ParameterBinderBase.bindingTracer.TraceScope("CALLING ProcessRecord")) { if (CmdletParameterBinderController.ObsoleteParameterWarningList != null && CmdletParameterBinderController.ObsoleteParameterWarningList.Count > 0) @@ -400,14 +396,13 @@ internal override void ProcessRecord() } catch (LoopFlowException) { - // Win8:84066 - Don't wrap LoopFlowException, we incorrectly raise a PipelineStoppedException + // Don't wrap LoopFlowException, we incorrectly raise a PipelineStoppedException // which gets caught by a script try/catch if we wrap here. throw; } - // 2004/03/18-JonN This is understood to be - // an FXCOP violation, cleared by KCwalina. - catch (Exception e) // Catch-all OK, 3rd party callout. + catch (Exception e) { + // Catch-all OK, 3rd party callout. exceptionToThrow = e; } finally diff --git a/src/System.Management.Automation/engine/CommandProcessorBase.cs b/src/System.Management.Automation/engine/CommandProcessorBase.cs index c6825cc6925..1d0128a8b38 100644 --- a/src/System.Management.Automation/engine/CommandProcessorBase.cs +++ b/src/System.Management.Automation/engine/CommandProcessorBase.cs @@ -5,8 +5,7 @@ using System.Collections.ObjectModel; using System.Management.Automation.Internal; using System.Management.Automation.Language; - -using Dbg = System.Management.Automation.Diagnostics; +using System.Runtime.InteropServices; namespace System.Management.Automation { @@ -46,6 +45,7 @@ internal CommandProcessorBase(CommandInfo commandInfo) string errorTemplate = expAttribute.ExperimentAction == ExperimentAction.Hide ? DiscoveryExceptions.ScriptDisabledWhenFeatureOn : DiscoveryExceptions.ScriptDisabledWhenFeatureOff; + string errorMsg = StringUtil.Format(errorTemplate, expAttribute.ExperimentName); ErrorRecord errorRecord = new ErrorRecord( new InvalidOperationException(errorMsg), @@ -54,6 +54,8 @@ internal CommandProcessorBase(CommandInfo commandInfo) commandInfo); throw new CmdletInvocationException(errorRecord); } + + HasCleanBlock = scriptCommand.ScriptBlock.HasCleanBlock; } CommandInfo = commandInfo; @@ -87,6 +89,11 @@ internal bool AddedToPipelineAlready /// internal CommandInfo CommandInfo { get; set; } + /// + /// Gets whether the command has a 'Clean' block defined. + /// + internal bool HasCleanBlock { get; } + /// /// This indicates whether this command processor is created from /// a script file. @@ -345,10 +352,7 @@ internal void SetCurrentScopeToExecutionScope() // Make sure we have a session state instance for this command. // If one hasn't been explicitly set, then use the session state // available on the engine execution context... - if (CommandSessionState == null) - { - CommandSessionState = Context.EngineSessionState; - } + CommandSessionState ??= Context.EngineSessionState; // Store off the current scope _previousScope = CommandSessionState.CurrentScope; @@ -371,13 +375,10 @@ internal void RestorePreviousScope() Context.EngineSessionState = _previousCommandSessionState; - if (_previousScope != null) - { - // Restore the scope but use the same session state instance we - // got it from because the command may have changed the execution context - // session state... - CommandSessionState.CurrentScope = _previousScope; - } + // Restore the scope but use the same session state instance we + // got it from because the command may have changed the execution context + // session state... + CommandSessionState.CurrentScope = _previousScope; } private SessionStateScope _previousScope; @@ -452,16 +453,14 @@ internal void DoPrepare(IDictionary psDefaultParameterValues) HandleObsoleteCommand(ObsoleteAttribute); } } - catch (Exception) + catch (InvalidComObjectException e) { - if (_useLocalScope) - { - // If we had an exception during Prepare, we're done trying to execute the command - // so the scope we created needs to release any resources it hold.s - CommandSessionState.RemoveScope(CommandScope); - } + // This type of exception could be thrown from parameter binding. + string msg = StringUtil.Format(ParserStrings.InvalidComObjectException, e.Message); + var newEx = new RuntimeException(msg, e); - throw; + newEx.SetErrorId("InvalidComObjectException"); + throw newEx; } finally { @@ -508,26 +507,23 @@ internal virtual void DoBegin() // The RedirectShellErrorOutputPipe flag is used by the V2 hosting API to force the // redirection. // - if (this.RedirectShellErrorOutputPipe || _context.ShellFunctionErrorOutputPipe != null) + if (RedirectShellErrorOutputPipe || _context.ShellFunctionErrorOutputPipe is not null) { - _context.ShellFunctionErrorOutputPipe = this.commandRuntime.ErrorOutputPipe; + _context.ShellFunctionErrorOutputPipe = commandRuntime.ErrorOutputPipe; } _context.CurrentCommandProcessor = this; + SetCurrentScopeToExecutionScope(); + using (commandRuntime.AllowThisCommandToWrite(true)) + using (ParameterBinderBase.bindingTracer.TraceScope("CALLING BeginProcessing")) { - using (ParameterBinderBase.bindingTracer.TraceScope( - "CALLING BeginProcessing")) + if (Context._debuggingMode > 0 && Command is not PSScriptCmdlet) { - SetCurrentScopeToExecutionScope(); - - if (Context._debuggingMode > 0 && Command is not PSScriptCmdlet) - { - Context.Debugger.CheckCommand(this.Command.MyInvocation); - } - - Command.DoBeginProcessing(); + Context.Debugger.CheckCommand(Command.MyInvocation); } + + Command.DoBeginProcessing(); } } catch (Exception e) @@ -589,20 +585,14 @@ internal virtual void Complete() try { using (commandRuntime.AllowThisCommandToWrite(true)) + using (ParameterBinderBase.bindingTracer.TraceScope("CALLING EndProcessing")) { - using (ParameterBinderBase.bindingTracer.TraceScope( - "CALLING EndProcessing")) - { - this.Command.DoEndProcessing(); - } + this.Command.DoEndProcessing(); } } - // 2004/03/18-JonN This is understood to be - // an FXCOP violation, cleared by KCwalina. catch (Exception e) { - // This cmdlet threw an exception, so - // wrap it and bubble it up. + // This cmdlet threw an exception, wrap it as needed and bubble it up. throw ManageInvocationException(e); } } @@ -631,44 +621,119 @@ internal void DoComplete() // The RedirectShellErrorOutputPipe flag is used by the V2 hosting API to force the // redirection. // - if (this.RedirectShellErrorOutputPipe || _context.ShellFunctionErrorOutputPipe != null) + if (RedirectShellErrorOutputPipe || _context.ShellFunctionErrorOutputPipe is not null) { - _context.ShellFunctionErrorOutputPipe = this.commandRuntime.ErrorOutputPipe; + _context.ShellFunctionErrorOutputPipe = commandRuntime.ErrorOutputPipe; } _context.CurrentCommandProcessor = this; - SetCurrentScopeToExecutionScope(); Complete(); } finally { - OnRestorePreviousScope(); - _context.ShellFunctionErrorOutputPipe = oldErrorOutputPipe; _context.CurrentCommandProcessor = oldCurrentCommandProcessor; - // Destroy the local scope at this point if there is one... - if (_useLocalScope && CommandScope != null) - { - CommandSessionState.RemoveScope(CommandScope); - } + RestorePreviousScope(); + } + } - // and the previous scope... - if (_previousScope != null) + protected virtual void CleanResource() + { + try + { + using (commandRuntime.AllowThisCommandToWrite(permittedToWriteToPipeline: true)) + using (ParameterBinderBase.bindingTracer.TraceScope("CALLING CleanResource")) { - // Restore the scope but use the same session state instance we - // got it from because the command may have changed the execution context - // session state... - CommandSessionState.CurrentScope = _previousScope; + Command.DoCleanResource(); } + } + catch (HaltCommandException) + { + throw; + } + catch (FlowControlException) + { + throw; + } + catch (Exception e) + { + // This cmdlet threw an exception, so wrap it and bubble it up. + throw ManageInvocationException(e); + } + } + + internal void DoCleanup() + { + // The property 'PropagateExceptionsToEnclosingStatementBlock' controls whether a general exception + // (an exception thrown from a .NET method invocation, or an expression like '1/0') will be turned + // into a terminating error, which will be propagated up and thus stop the rest of the running script. + // It is usually used by TryStatement and TrapStatement, which makes the general exception catch-able. + // + // For the 'Clean' block, we don't want to bubble up the general exception when the command is enclosed + // in a TryStatement or has TrapStatement accompanying, because no exception can escape from 'Clean' and + // thus it's pointless to bubble up the general exception in this case. + // + // Therefore we set this property to 'false' here to mask off the previous setting that could be from a + // TryStatement or TrapStatement. Example: + // PS:1> function b { end {} clean { 1/0; Write-Host 'clean' } } + // PS:2> b + // RuntimeException: Attempted to divide by zero. + // clean + // ## Note that, outer 'try/trap' doesn't affect the general exception happens in 'Clean' block. + // ## so its behavior is consistent regardless of whether the command is enclosed by 'try/catch' or not. + // PS:3> try { b } catch { 'outer catch' } + // RuntimeException: Attempted to divide by zero. + // clean + // + // Be noted that, this doesn't affect the TryStatement/TrapStatement within the 'Clean' block. Example: + // ## 'try/trap' within 'Clean' block makes the general exception catch-able. + // PS:3> function a { end {} clean { try { 1/0; Write-Host 'clean' } catch { Write-Host "caught: $_" } } } + // PS:4> a + // caught: Attempted to divide by zero. + bool oldExceptionPropagationState = _context.PropagateExceptionsToEnclosingStatementBlock; + _context.PropagateExceptionsToEnclosingStatementBlock = false; - // Restore the previous session state - if (_previousCommandSessionState != null) + Pipe oldErrorOutputPipe = _context.ShellFunctionErrorOutputPipe; + CommandProcessorBase oldCurrentCommandProcessor = _context.CurrentCommandProcessor; + + try + { + if (RedirectShellErrorOutputPipe || _context.ShellFunctionErrorOutputPipe is not null) { - Context.EngineSessionState = _previousCommandSessionState; + _context.ShellFunctionErrorOutputPipe = commandRuntime.ErrorOutputPipe; } + + _context.CurrentCommandProcessor = this; + SetCurrentScopeToExecutionScope(); + CleanResource(); } + finally + { + _context.PropagateExceptionsToEnclosingStatementBlock = oldExceptionPropagationState; + _context.ShellFunctionErrorOutputPipe = oldErrorOutputPipe; + _context.CurrentCommandProcessor = oldCurrentCommandProcessor; + + RestorePreviousScope(); + } + } + + internal void ReportCleanupError(Exception exception) + { + var error = exception is IContainsErrorRecord icer + ? icer.ErrorRecord + : new ErrorRecord(exception, "Clean.ReportException", ErrorCategory.NotSpecified, targetObject: null); + + PSObject errorWrap = PSObject.AsPSObject(error); + errorWrap.WriteStream = WriteStreamType.Error; + + var errorPipe = commandRuntime.ErrorMergeTo == MshCommandRuntime.MergeDataStream.Output + ? commandRuntime.OutputPipe + : commandRuntime.ErrorOutputPipe; + + errorPipe.Add(errorWrap); + _context.QuestionMarkVariableValue = false; } /// @@ -777,23 +842,16 @@ internal PipelineStoppedException ManageInvocationException(Exception e) { do // false loop { - ProviderInvocationException pie = e as ProviderInvocationException; - if (pie != null) + if (e is ProviderInvocationException pie) { - // If a ProviderInvocationException occurred, - // discard the ProviderInvocationException and - // re-wrap in CmdletProviderInvocationException - e = new CmdletProviderInvocationException( - pie, - Command.MyInvocation); + // If a ProviderInvocationException occurred, discard the ProviderInvocationException + // and re-wrap it in CmdletProviderInvocationException. + e = new CmdletProviderInvocationException(pie, Command.MyInvocation); break; } - // 1021203-2005/05/09-JonN - // HaltCommandException will cause the command - // to stop, but not be reported as an error. - // 906445-2005/05/16-JonN - // FlowControlException should not be wrapped + // HaltCommandException will cause the command to stop, but not be reported as an error. + // FlowControlException should not be wrapped. if (e is PipelineStoppedException || e is CmdletInvocationException || e is ActionPreferenceStopException @@ -813,9 +871,7 @@ internal PipelineStoppedException ManageInvocationException(Exception e) } // wrap all other exceptions - e = new CmdletInvocationException( - e, - Command.MyInvocation); + e = new CmdletInvocationException(e, Command.MyInvocation); } while (false); // commandRuntime.ManageException will always throw PipelineStoppedException @@ -943,15 +999,27 @@ public void Dispose() private void Dispose(bool disposing) { if (_disposed) + { return; + } if (disposing) { - // 2004/03/05-JonN Look into using metadata to check - // whether IDisposable is implemented, in order to avoid - // this expensive reflection cast. - IDisposable id = Command as IDisposable; - if (id != null) + if (UseLocalScope) + { + // Clean up the PS drives that are associated with this local scope. + // This operation may be needed at multiple stages depending on whether the 'clean' block is declared: + // 1. when there is a 'clean' block, it needs to be done only after 'clean' block runs, because the scope + // needs to be preserved until the 'clean' block finish execution. + // 2. when there is no 'clean' block, it needs to be done when + // (1) there is any exception thrown from 'DoPrepare()', 'DoBegin()', 'DoExecute()', or 'DoComplete'; + // (2) OR, the command runs to the end successfully; + // Doing this cleanup at those multiple stages is cumbersome. Since we will always dispose the command in + // the end, doing this cleanup here will cover all the above cases. + CommandSessionState.RemoveScope(CommandScope); + } + + if (Command is IDisposable id) { id.Dispose(); } diff --git a/src/System.Management.Automation/engine/CommandSearcher.cs b/src/System.Management.Automation/engine/CommandSearcher.cs index 9745d2aa9ad..cf798ddabbd 100644 --- a/src/System.Management.Automation/engine/CommandSearcher.cs +++ b/src/System.Management.Automation/engine/CommandSearcher.cs @@ -158,7 +158,7 @@ public bool MoveNext() } else { - // Ok see it it's in the applications list + // Ok, see if it's in the applications list foreach (string path in _context.EngineSessionState.Applications) { if (checkPath(path, _commandName)) @@ -928,10 +928,7 @@ private static bool ShouldSkipCommandResolutionForConstrainedLanguage(CommandInf } } - if (module == null) - { - module = modules[0]; - } + module ??= modules[0]; } return module; diff --git a/src/System.Management.Automation/engine/CommonCommandParameters.cs b/src/System.Management.Automation/engine/CommonCommandParameters.cs index 376b9d0adbf..c3630fe0b43 100644 --- a/src/System.Management.Automation/engine/CommonCommandParameters.cs +++ b/src/System.Management.Automation/engine/CommonCommandParameters.cs @@ -59,7 +59,7 @@ public SwitchParameter Verbose /// /// /// This parameter tells the command to provide Programmer/Support type - /// messages to understand what is really occuring and give the user the + /// messages to understand what is really occurring and give the user the /// opportunity to stop or debug the situation. /// [Parameter] diff --git a/src/System.Management.Automation/engine/CompiledCommandParameter.cs b/src/System.Management.Automation/engine/CompiledCommandParameter.cs index 2f17fca8fcd..d2ca1aedd91 100644 --- a/src/System.Management.Automation/engine/CompiledCommandParameter.cs +++ b/src/System.Management.Automation/engine/CompiledCommandParameter.cs @@ -440,8 +440,7 @@ private void ProcessAttribute( ValidateArgumentsAttribute validateAttr = attribute as ValidateArgumentsAttribute; if (validateAttr != null) { - if (validationAttributes == null) - validationAttributes = new Collection(); + validationAttributes ??= new Collection(); validationAttributes.Add(validateAttr); if ((attribute is ValidateNotNullAttribute) || (attribute is ValidateNotNullOrEmptyAttribute)) { @@ -473,8 +472,7 @@ private void ProcessAttribute( ArgumentTransformationAttribute argumentAttr = attribute as ArgumentTransformationAttribute; if (argumentAttr != null) { - if (argTransformationAttributes == null) - argTransformationAttributes = new Collection(); + argTransformationAttributes ??= new Collection(); argTransformationAttributes.Add(argumentAttr); return; } diff --git a/src/System.Management.Automation/engine/CoreAdapter.cs b/src/System.Management.Automation/engine/CoreAdapter.cs index 1a623e92f1e..498cc261e29 100644 --- a/src/System.Management.Automation/engine/CoreAdapter.cs +++ b/src/System.Management.Automation/engine/CoreAdapter.cs @@ -1377,6 +1377,27 @@ internal static MethodInformation FindBestMethod( return methodInfo; } + private static Type[] ResolveGenericTypeParameters(object[] genericTypeParameters) + { + if (genericTypeParameters is null || genericTypeParameters.Length == 0) + { + return null; + } + + Type[] genericParamTypes = new Type[genericTypeParameters.Length]; + for (int i = 0; i < genericTypeParameters.Length; i++) + { + genericParamTypes[i] = genericTypeParameters[i] switch + { + Type paramType => paramType, + ITypeName paramTypeName => TypeOps.ResolveTypeName(paramTypeName, paramTypeName.Extent), + _ => throw new ArgumentException("Unexpected value"), + }; + } + + return genericParamTypes; + } + private static MethodInformation FindBestMethodImpl( MethodInformation[] methods, PSMethodInvocationConstraints invocationConstraints, @@ -1394,59 +1415,84 @@ private static MethodInformation FindBestMethodImpl( // be turned into an array. // We also skip the optimization if the number of arguments and parameters is different // so we let the loop deal with possible optional parameters. - if ((methods.Length == 1) && - (!methods[0].hasVarArgs) && - (!methods[0].isGeneric) && - (methods[0].method == null || !(methods[0].method.DeclaringType.IsGenericTypeDefinition)) && + if (methods.Length == 1 + && !methods[0].hasVarArgs // generic methods need to be double checked in a loop below - generic methods can be rejected if type inference fails - (methods[0].parameters.Length == arguments.Length)) + && !methods[0].isGeneric + && (methods[0].method is null || !methods[0].method.DeclaringType.IsGenericTypeDefinition) + && methods[0].parameters.Length == arguments.Length) { return methods[0]; } - Type[] argumentTypes = arguments.Select(EffectiveArgumentType).ToArray(); - List candidates = new List(); + Type[] genericParamTypes = ResolveGenericTypeParameters(invocationConstraints?.GenericTypeParameters); + var candidates = new List(); + for (int i = 0; i < methods.Length; i++) { - MethodInformation method = methods[i]; + MethodInformation methodInfo = methods[i]; - if (method.method != null && method.method.DeclaringType.IsGenericTypeDefinition) + if (methodInfo.method?.DeclaringType.IsGenericTypeDefinition == true + || (!methodInfo.isGeneric && genericParamTypes is not null)) { - continue; // skip methods defined by an *open* generic type + // If method is defined by an *open* generic type, or + // if generic parameters were provided and this method isn't generic, skip it. + continue; } - if (method.isGeneric) + if (methodInfo.isGeneric) { - Type[] argumentTypesForTypeInference = new Type[argumentTypes.Length]; - Array.Copy(argumentTypes, argumentTypesForTypeInference, argumentTypes.Length); - if (invocationConstraints != null && invocationConstraints.ParameterTypes != null) + if (genericParamTypes is not null) + { + try + { + // This cast is safe, because + // 1. Only ConstructorInfo and MethodInfo derive from MethodBase + // 2. ConstructorInfo.IsGenericMethod is always false + var originalMethod = (MethodInfo)methodInfo.method; + methodInfo = new MethodInformation( + originalMethod.MakeGenericMethod(genericParamTypes), + parametersToIgnore: 0); + } + catch (ArgumentException) + { + // Just skip this possibility if the generic type parameters can't be used to make + // a valid generic method here. + continue; + } + } + else { - int parameterIndex = 0; - foreach (Type typeConstraintFromCallSite in invocationConstraints.ParameterTypes) + // Infer the generic method when generic parameter types are not specified. + Type[] argumentTypes = arguments.Select(EffectiveArgumentType).ToArray(); + Type[] paramConstraintTypes = invocationConstraints?.ParameterTypes; + + if (paramConstraintTypes is not null) { - if (typeConstraintFromCallSite != null) + for (int k = 0; k < paramConstraintTypes.Length; k++) { - argumentTypesForTypeInference[parameterIndex] = typeConstraintFromCallSite; + if (paramConstraintTypes[k] is not null) + { + argumentTypes[k] = paramConstraintTypes[k]; + } } - - parameterIndex++; } - } - method = TypeInference.Infer(method, argumentTypesForTypeInference); - if (method == null) - { - // Skip generic methods for which we cannot infer type arguments - continue; + methodInfo = TypeInference.Infer(methodInfo, argumentTypes); + if (methodInfo is null) + { + // Skip generic methods for which we cannot infer type arguments + continue; + } } } - if (!IsInvocationTargetConstraintSatisfied(method, invocationConstraints)) + if (!IsInvocationTargetConstraintSatisfied(methodInfo, invocationConstraints)) { continue; } - ParameterInformation[] parameters = method.parameters; + ParameterInformation[] parameters = methodInfo.parameters; if (arguments.Length != parameters.Length) { // Skip methods w/ an incorrect # of arguments. @@ -1454,7 +1500,7 @@ private static MethodInformation FindBestMethodImpl( if (arguments.Length > parameters.Length) { // If too many args,it's only OK if the method is varargs. - if (!method.hasVarArgs) + if (!methodInfo.hasVarArgs) { continue; } @@ -1462,12 +1508,12 @@ private static MethodInformation FindBestMethodImpl( else { // Too few args, OK if there are optionals, or varargs with the param array omitted - if (!method.hasOptional && (!method.hasVarArgs || (arguments.Length + 1) != parameters.Length)) + if (!methodInfo.hasOptional && (!methodInfo.hasVarArgs || (arguments.Length + 1) != parameters.Length)) { continue; } - if (method.hasOptional) + if (methodInfo.hasOptional) { // Count optionals. This code is rarely hit, mainly when calling code in the // assembly Microsoft.VisualBasic. If it were more frequent, the optional count @@ -1490,7 +1536,7 @@ private static MethodInformation FindBestMethodImpl( } } - OverloadCandidate candidate = new OverloadCandidate(method, arguments.Length); + OverloadCandidate candidate = new OverloadCandidate(methodInfo, arguments.Length); for (int j = 0; candidate != null && j < parameters.Length; j++) { ParameterInformation parameter = parameters[j]; @@ -1581,7 +1627,7 @@ private static MethodInformation FindBestMethodImpl( if (candidates.Count == 0) { - if ((methods.Length > 0) && (methods.All(static m => m.method != null && m.method.DeclaringType.IsGenericTypeDefinition && m.method.IsStatic))) + if (methods.Length > 0 && methods.All(static m => m.method != null && m.method.DeclaringType.IsGenericTypeDefinition && m.method.IsStatic)) { errorId = "CannotInvokeStaticMethodOnUninstantiatedGenericType"; errorMsg = string.Format( @@ -1590,6 +1636,16 @@ private static MethodInformation FindBestMethodImpl( methods[0].method.DeclaringType.FullName); return null; } + else if (genericParamTypes is not null) + { + errorId = "MethodCountCouldNotFindBestGeneric"; + errorMsg = string.Format( + ExtendedTypeSystem.MethodGenericArgumentCountException, + methods[0].method.Name, + genericParamTypes.Length, + arguments.Length); + return null; + } else { errorId = "MethodCountCouldNotFindBest"; @@ -2202,10 +2258,7 @@ internal object Invoke(object target, object[] arguments) if (!_useReflection) { - if (_methodInvoker == null) - { - _methodInvoker = GetMethodInvoker(methodInfo); - } + _methodInvoker ??= GetMethodInvoker(methodInfo); if (_methodInvoker != null) { @@ -2991,10 +3044,7 @@ internal override bool IsHidden { get { - if (_isHidden == null) - { - _isHidden = member.GetCustomAttributes(typeof(HiddenAttribute), inherit: false).Length != 0; - } + _isHidden ??= member.GetCustomAttributes(typeof(HiddenAttribute), inherit: false).Length != 0; return _isHidden.Value; } @@ -3809,6 +3859,33 @@ private static bool PropertyIsStatic(PSProperty property) return entry.isStatic; } + /// + /// Get the string representation of the default value of passed-in parameter. + /// + /// ParameterInfo containing the parameter's default value. + /// String representation of the parameter's default value. + private static string GetDefaultValueStringRepresentation(ParameterInfo parameterInfo) + { + var parameterType = parameterInfo.ParameterType; + var parameterDefaultValue = parameterInfo.DefaultValue; + + if (parameterDefaultValue == null) + { + return (parameterType.IsValueType || parameterType.IsGenericMethodParameter) + ? "default" + : "null"; + } + + if (parameterType.IsEnum) + { + return string.Format(CultureInfo.InvariantCulture, "{0}.{1}", parameterType.ToString(), parameterDefaultValue.ToString()); + } + + return (parameterDefaultValue is string) + ? string.Format(CultureInfo.InvariantCulture, "\"{0}\"", parameterDefaultValue.ToString()) + : parameterDefaultValue.ToString(); + } + #endregion auxiliary methods and classes #region virtual @@ -4401,6 +4478,13 @@ internal static string GetMethodInfoOverloadDefinition(string memberName, Method builder.Append(ToStringCodeMethods.Type(parameterType)); builder.Append(' '); builder.Append(parameter.Name); + + if (parameter.HasDefaultValue) + { + builder.Append(" = "); + builder.Append(GetDefaultValueStringRepresentation(parameter)); + } + builder.Append(", "); } @@ -5884,7 +5968,7 @@ private static MethodInfo Infer(MethodInfo genericMethod, ICollection type try { MethodInfo instantiatedMethod = genericMethod.MakeGenericMethod(inferredTypeParameters.ToArray()); - s_tracer.WriteLine("Inference succesful: {0}", instantiatedMethod); + s_tracer.WriteLine("Inference successful: {0}", instantiatedMethod); return instantiatedMethod; } catch (ArgumentException e) diff --git a/src/System.Management.Automation/engine/ErrorPackage.cs b/src/System.Management.Automation/engine/ErrorPackage.cs index 1f17a501736..5ad138079f0 100644 --- a/src/System.Management.Automation/engine/ErrorPackage.cs +++ b/src/System.Management.Automation/engine/ErrorPackage.cs @@ -1026,10 +1026,7 @@ public ErrorRecord( throw PSTraceSource.NewArgumentNullException(nameof(exception)); } - if (errorId == null) - { - errorId = string.Empty; - } + errorId ??= string.Empty; // targetObject may be null _error = exception; diff --git a/src/System.Management.Automation/engine/EventManager.cs b/src/System.Management.Automation/engine/EventManager.cs index 3df9153f4e7..78304819289 100644 --- a/src/System.Management.Automation/engine/EventManager.cs +++ b/src/System.Management.Automation/engine/EventManager.cs @@ -862,10 +862,7 @@ private void UnsubscribeEvent(PSEventSubscriber subscriber, bool skipDraining) } // Stop the job - if (subscriber.Action != null) - { - subscriber.Action.NotifyJobStopped(); - } + subscriber.Action?.NotifyJobStopped(); lock (_eventSubscribers) { @@ -1532,10 +1529,7 @@ public void Dispose(bool disposing) { lock (_eventSubscribers) { - if (_timer != null) - { - _timer.Dispose(); - } + _timer?.Dispose(); foreach (PSEventSubscriber currentSubscriber in _eventSubscribers.Keys.ToArray()) { diff --git a/src/System.Management.Automation/engine/ExecutionContext.cs b/src/System.Management.Automation/engine/ExecutionContext.cs index cfcc417de4a..0f2fb0822b5 100644 --- a/src/System.Management.Automation/engine/ExecutionContext.cs +++ b/src/System.Management.Automation/engine/ExecutionContext.cs @@ -53,22 +53,12 @@ internal ScriptDebugger Debugger /// internal void ResetManagers() { - if (_debugger != null) - { - _debugger.ResetDebugger(); - } - - if (Events != null) - { - Events.Dispose(); - } + _debugger?.ResetDebugger(); + Events?.Dispose(); Events = new PSLocalEventManager(this); - if (this.transactionManager != null) - { - this.transactionManager.Dispose(); - } + this.transactionManager?.Dispose(); this.transactionManager = new PSTransactionManager(); } /// @@ -114,10 +104,7 @@ internal bool PSDebugTraceStep // Helper for generated code to handle running w/ no execution context internal static bool IsStrictVersion(ExecutionContext context, int majorVersion) { - if (context == null) - { - context = LocalPipeline.GetExecutionContextFromTLS(); - } + context ??= LocalPipeline.GetExecutionContextFromTLS(); return (context != null) && context.IsStrictVersion(majorVersion); } @@ -219,10 +206,7 @@ internal ProviderNames ProviderNames { get { - if (_providerNames == null) - { - _providerNames = new SingleShellProviderNames(); - } + _providerNames ??= new SingleShellProviderNames(); return _providerNames; } @@ -379,8 +363,7 @@ internal static bool IsMarkedAsUntrusted(object value) var baseValue = PSObject.Base(value); if (baseValue != null && baseValue != NullString.Value) { - object unused; - result = UntrustedObjects.TryGetValue(baseValue, out unused); + result = UntrustedObjects.TryGetValue(baseValue, out _); } return result; @@ -525,9 +508,7 @@ internal object GetVariableValue(VariablePath path) /// internal object GetVariableValue(VariablePath path, object defaultValue) { - CmdletProviderContext context; - SessionStateScope scope; - return EngineSessionState.GetVariableValue(path, out context, out scope) ?? defaultValue; + return EngineSessionState.GetVariableValue(path, out _, out _) ?? defaultValue; } /// @@ -612,19 +593,15 @@ private void CheckActionPreference(VariablePath preferenceVariablePath, ActionPr /// internal bool GetBooleanPreference(VariablePath preferenceVariablePath, bool defaultPref, out bool defaultUsed) { - CmdletProviderContext context = null; - SessionStateScope scope = null; - object val = EngineSessionState.GetVariableValue(preferenceVariablePath, out context, out scope); - if (val == null) + object val = EngineSessionState.GetVariableValue(preferenceVariablePath, out _, out _); + if (val is null) { defaultUsed = true; return defaultPref; } - bool converted = defaultPref; - defaultUsed = !LanguagePrimitives.TryConvertTo - (val, out converted); - return (defaultUsed) ? defaultPref : converted; + defaultUsed = !LanguagePrimitives.TryConvertTo(val, out bool converted); + return defaultUsed ? defaultPref : converted; } #endregion GetSetVariable methods @@ -789,11 +766,6 @@ internal Pipe RedirectErrorPipe(Pipe newPipe) return oldPipe; } - internal void RestoreErrorPipe(Pipe pipe) - { - ShellFunctionErrorOutputPipe = pipe; - } - /// /// Reset all of the redirection book keeping variables. This routine should be called when starting to /// execute a script. @@ -846,15 +818,13 @@ internal void ResetRedirection() internal void AppendDollarError(object obj) { ErrorRecord objAsErrorRecord = obj as ErrorRecord; - if (objAsErrorRecord == null && obj is not Exception) + if (objAsErrorRecord is null && obj is not Exception) { Diagnostics.Assert(false, "Object to append was neither an ErrorRecord nor an Exception in ExecutionContext.AppendDollarError"); return; } - object old = this.DollarErrorVariable; - ArrayList arraylist = old as ArrayList; - if (arraylist == null) + if (DollarErrorVariable is not ArrayList arraylist) { Diagnostics.Assert(false, "$error should be a global constant ArrayList"); return; @@ -1175,22 +1145,12 @@ internal void RunspaceClosingNotification() { EngineSessionState.RunspaceClosingNotification(); - if (_debugger != null) - { - _debugger.Dispose(); - } - - if (Events != null) - { - Events.Dispose(); - } + _debugger?.Dispose(); + Events?.Dispose(); Events = null; - if (this.transactionManager != null) - { - this.transactionManager.Dispose(); - } + this.transactionManager?.Dispose(); this.transactionManager = null; } @@ -1282,55 +1242,150 @@ internal PSTransactionManager TransactionManager internal PSTransactionManager transactionManager; - internal Assembly AddAssembly(string name, string filename, out Exception error) - { - Assembly loadedAssembly = LoadAssembly(name, filename, out error); - - if (loadedAssembly == null) - return null; - - if (AssemblyCache.ContainsKey(loadedAssembly.FullName)) + /// + /// This method is used for assembly loading requests stemmed from 'InitialSessionState' binding and module loading. + /// + /// Source of the assembly loading request, should be a module name when specified. + /// Name of the assembly to be loaded. + /// Path of the assembly to be loaded. + /// Exception that is caught when the loading fails. + internal Assembly AddAssembly(string source, string assemblyName, string filePath, out Exception error) + { + // Search the cache by the path, and return the assembly if we find it. + // It's common to have two loading requests for the same assembly when loading a module -- the first time for + // resolving a binary module path, and the second time for actually processing that module. + // + // That's not a problem when all the module assemblies are loaded into the default ALC. But in a scenario where + // a module tries to hide its nested/root binary modules in a custom ALC, that will become a problem. This is + // because: + // in that scenario, the module will usually setup a handler to load the specific assemblies to the custom ALC, + // and that will be how the first loading request gets served. However, after the module path is resolved with + // the first loading, the path will be used for the second loading upon real module processing. Since we prefer + // loading-by-path over loading-by-name in the 'LoadAssembly' call, we will end up loading the same assembly in + // the default ALC (because we use 'Assembly.LoadFrom' which always loads an assembly to the default ALC) if we + // do not search in the cache first. That will break the scenario, because the module means to isolate all its + // dependencies from the default ALC, and it failed to do so. + // + // Therefore, we need to search the cache first. The reason we use path as the key is to make sure the request + // is for exactly the same assembly. The same assembly file should not be loaded into different ALC's by module + // loading within the same PowerShell session (Runspace). + // + // An example module targeting the abovementioned scenario will likely have the following file structure: + // IsolatedModule + // │ IsolatedModule.psd1 (has 'NestedModules = @('Test.Isolated.Init.dll', 'Test.Isolated.Nested.dll')') + // │ Test.Isolated.Init.dll (contains the custom ALC and code to setup 'Resolving' handler) + // └───Dependencies (folder under module base) + // Newtonsoft.Json.dll (version 10.0.0.0 dependency) + // Test.Isolated.Nested.dll (nested binary module referencing the particular dependency) + // + // In this example, the following events will happen in sequence: + // 1. PowerShell is able to find 'Test.Isolated.Init.dll' under module base folder, so it will be loaded into + // the default ALC as expected and setup the 'Resolving' handler via the 'OnImport' call. + // 2. PowerShell cannot find 'Test.Isolated.Nested.dll' under the module base folder, so it will call the method + // 'FixFileName(.., bool canLoadAssembly)' to resolve the path of this binary module. + // This particular overload will attempt to load the assembly by name, which will be served by the 'Resolving' + // handler that was setup in the step 1. So, the assembly will be loaded into the custom ALC and insert to the + // assembly cache. + // 3. Path of the nested module 'Test.Isolated.Init.dll' now has been resolved by the step 2 (assembly.Location). + // Now it's time to actually load this binary module for processing in the method 'LoadBinaryModule', which + // will make a call to this method with the resolved assembly file path. + // At this poin, we will have to query the cache first, instead of calling 'LoadAssembly' directly, to make sure + // that the assembly instance loaded in the custom ALC in step 2 gets returned back. Otherwise, the same assembly + // file will be loaded in the default ALC because 'Assembly.LoadFrom' is used in 'LoadAssembly' and that API will + // always load an assembly file to the default ALC, and that will break this scenario. + if (TryGetFromAssemblyCache(source, filePath, out Assembly loadedAssembly)) { - // we should ignore this assembly. + error = null; return loadedAssembly; } - // We will cache the assembly by both full name and - // file name - AssemblyCache.Add(loadedAssembly.FullName, loadedAssembly); - if (AssemblyCache.ContainsKey(loadedAssembly.GetName().Name)) + // Attempt to load the requested assembly, first by path then by name. + loadedAssembly = LoadAssembly(assemblyName, filePath, out error); + if (loadedAssembly is not null) { - // we should ignore this assembly. - return loadedAssembly; + AddToAssemblyCache(source, loadedAssembly); } - AssemblyCache.Add(loadedAssembly.GetName().Name, loadedAssembly); return loadedAssembly; } - internal void RemoveAssembly(string name) + /// + /// Add a loaded assembly to the 'AssemblyCache'. + /// The is used as a prefix for the key to make it easy to remove all associated + /// assemblies from the cache when a module gets unloaded. + /// + /// The source where the assembly comes from, should be a module name when specified. + /// The assembly we try to cache. + internal void AddToAssemblyCache(string source, Assembly assembly) { - Assembly loadedAssembly; - if (AssemblyCache.TryGetValue(name, out loadedAssembly) && loadedAssembly != null) + // Try caching the assembly by its location if possible. + // When it's a dynamic assembly, we use it's full name. This could happen with 'Import-Module -Assembly'. + string key = string.IsNullOrEmpty(assembly.Location) ? assembly.FullName : assembly.Location; + + // When the assembly is from a module loading, we prefix the key with the source, + // so we can remove it from the cache when the module gets unloaded. + if (!string.IsNullOrEmpty(source)) { - AssemblyCache.Remove(name); + // Both 'source' and 'key' are of the string type, so no need to specify 'InvariantCulture'. + key = $"{source}@{key}"; + } - AssemblyCache.Remove(loadedAssembly.GetName().Name); + AssemblyCache.TryAdd(key, assembly); + } + + /// + /// Remove all cache entries that are associated with the specified source. + /// + internal void RemoveFromAssemblyCache(string source) + { + if (string.IsNullOrEmpty(source)) + { + return; + } + + var keysToRemove = new List(); + string prefix = $"{source}@"; + + foreach (string key in AssemblyCache.Keys) + { + if (key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) + { + keysToRemove.Add(key); + } } + + foreach (string key in keysToRemove) + { + AssemblyCache.Remove(key); + } + } + + /// + /// Try to get an assembly from the cache. + /// + private bool TryGetFromAssemblyCache(string source, string filePath, out Assembly assembly) + { + if (string.IsNullOrEmpty(filePath)) + { + assembly = null; + return false; + } + + // Both 'source' and 'filePath' are of the string type, so no need to specify 'InvariantCulture'. + string key = string.IsNullOrEmpty(source) ? filePath : $"{source}@{filePath}"; + return AssemblyCache.TryGetValue(key, out assembly); } - [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadWithPartialName")] - [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")] - internal static Assembly LoadAssembly(string name, string filename, out Exception error) + private static Assembly LoadAssembly(string name, string filePath, out Exception error) { // First we try to load the assembly based on the filename Assembly loadedAssembly = null; error = null; - if (!string.IsNullOrEmpty(filename)) + if (!string.IsNullOrEmpty(filePath)) { try { - loadedAssembly = Assembly.LoadFrom(filename); + loadedAssembly = Assembly.LoadFrom(filePath); return loadedAssembly; } catch (FileNotFoundException fileNotFound) @@ -1502,8 +1557,7 @@ internal void ReportEngineStartupError(ErrorRecord errorRecord) try { Cmdlet currentRunningModuleCommand; - string unused; - if (IsModuleCommandCurrentlyRunning(out currentRunningModuleCommand, out unused)) + if (IsModuleCommandCurrentlyRunning(out currentRunningModuleCommand, out _)) { currentRunningModuleCommand.WriteError(errorRecord); } @@ -1568,23 +1622,6 @@ internal ExecutionContext(AutomationEngine engine, PSHost hostInterface, Initial private void InitializeCommon(AutomationEngine engine, PSHost hostInterface) { Engine = engine; -#if !CORECLR// System.AppDomain is not in CoreCLR - // Set the assembly resolve handler if it isn't already set... - if (!_assemblyEventHandlerSet) - { - // we only want to set the event handler once for the entire app domain... - lock (lockObject) - { - // Need to check again inside the lock due to possibility of a race condition... - if (!_assemblyEventHandlerSet) - { - AppDomain currentAppDomain = AppDomain.CurrentDomain; - currentAppDomain.AssemblyResolve += new ResolveEventHandler(PowerShellAssemblyResolveHandler); - _assemblyEventHandlerSet = true; - } - } - } -#endif Events = new PSLocalEventManager(this); transactionManager = new PSTransactionManager(); _debugger = new ScriptDebugger(this); @@ -1592,52 +1629,20 @@ private void InitializeCommon(AutomationEngine engine, PSHost hostInterface) EngineHostInterface = hostInterface as InternalHost ?? new InternalHost(hostInterface, this); // Hook up the assembly cache - AssemblyCache = new Dictionary(); + AssemblyCache = new Dictionary(StringComparer.OrdinalIgnoreCase); // Initialize the fixed toplevel session state and the current session state TopLevelSessionState = EngineSessionState = new SessionStateInternal(this); - if (AuthorizationManager == null) - { - // if authorizationmanager==null, this means the configuration - // explicitly asked for dummy authorization manager. - AuthorizationManager = new AuthorizationManager(null); - } + // if authorizationmanager==null, this means the configuration + // explicitly asked for dummy authorization manager. + AuthorizationManager ??= new AuthorizationManager(null); // Set up the module intrinsics Modules = new ModuleIntrinsics(this); } private static readonly object lockObject = new object(); - -#if !CORECLR // System.AppDomain is not in CoreCLR - private static bool _assemblyEventHandlerSet = false; - - /// - /// AssemblyResolve event handler that will look in the assembly cache to see - /// if the named assembly has been loaded. This is necessary so that assemblies loaded - /// with LoadFrom, which are in a different loaded context than Load, can still be used to - /// resolve types. - /// - /// The event sender. - /// The event args. - /// The resolve assembly or null if not found. - private static Assembly PowerShellAssemblyResolveHandler(object sender, ResolveEventArgs args) - { - ExecutionContext ecFromTLS = Runspaces.LocalPipeline.GetExecutionContextFromTLS(); - if (ecFromTLS != null) - { - if (ecFromTLS.AssemblyCache != null) - { - Assembly assembly; - ecFromTLS.AssemblyCache.TryGetValue(args.Name, out assembly); - return assembly; - } - } - - return null; - } -#endif } /// diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs index 9268003ee87..517b153d7ad 100644 --- a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs +++ b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs @@ -21,7 +21,7 @@ public class ExperimentalFeature #region Const Members internal const string EngineSource = "PSEngine"; - internal const string PSNativeCommandArgumentPassingFeatureName = "PSNativeCommandArgumentPassing"; + internal const string PSNativeCommandErrorActionPreferenceFeatureName = "PSNativeCommandErrorActionPreference"; #endregion @@ -107,21 +107,15 @@ static ExperimentalFeature() new ExperimentalFeature( name: "PSCommandNotFoundSuggestion", description: "Recommend potential commands based on fuzzy search on a CommandNotFoundException"), - new ExperimentalFeature( - name: "PSNativePSPathResolution", - description: "Convert PSPath to filesystem path, if possible, for native commands"), new ExperimentalFeature( name: "PSSubsystemPluginModel", description: "A plugin model for registering and un-registering PowerShell subsystems"), - new ExperimentalFeature( - name: PSNativeCommandArgumentPassingFeatureName, - description: "Use ArgumentList when invoking a native command"), new ExperimentalFeature( name: "PSLoadAssemblyFromNativeCode", description: "Expose an API to allow assembly loading from native code"), new ExperimentalFeature( - name: "PSAnsiRenderingFileInfo", - description: "Enable coloring for FileInfo objects"), + name: PSNativeCommandErrorActionPreferenceFeatureName, + description: "Native commands with non-zero exit codes issue errors according to $ErrorActionPreference when $PSNativeCommandUseErrorActionPreference is $true") }; EngineExperimentalFeatures = new ReadOnlyCollection(engineFeatures); diff --git a/src/System.Management.Automation/engine/ExtendedTypeSystemException.cs b/src/System.Management.Automation/engine/ExtendedTypeSystemException.cs index ac99127ab29..9ed85accb4f 100644 --- a/src/System.Management.Automation/engine/ExtendedTypeSystemException.cs +++ b/src/System.Management.Automation/engine/ExtendedTypeSystemException.cs @@ -647,14 +647,11 @@ public ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - _errorId, - ErrorCategory.InvalidArgument, - null); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + _errorId, + ErrorCategory.InvalidArgument, + null); return _errorRecord; } diff --git a/src/System.Management.Automation/engine/ExternalScriptInfo.cs b/src/System.Management.Automation/engine/ExternalScriptInfo.cs index bbd49c76d36..bcd75bf85f3 100644 --- a/src/System.Management.Automation/engine/ExternalScriptInfo.cs +++ b/src/System.Management.Automation/engine/ExternalScriptInfo.cs @@ -384,7 +384,7 @@ internal override bool ImplementsDynamicParameters // If we got here, there was some sort of parsing exception. We'll just // ignore it and assume the script does not implement dynamic parameters. - // Futhermore, we'll clear out the fields so that the next attempt to + // Furthermore, we'll clear out the fields so that the next attempt to // access ScriptBlock will result in an exception that doesn't get ignored. _scriptBlock = null; _scriptContents = null; @@ -516,32 +516,45 @@ private void ReadScriptContents() using (FileStream readerStream = new FileStream(_path, FileMode.Open, FileAccess.Read)) { Encoding defaultEncoding = ClrFacade.GetDefaultEncoding(); - Microsoft.Win32.SafeHandles.SafeFileHandle safeFileHandle = readerStream.SafeFileHandle; using (StreamReader scriptReader = new StreamReader(readerStream, defaultEncoding)) { _scriptContents = scriptReader.ReadToEnd(); _originalEncoding = scriptReader.CurrentEncoding; - // Check if this came from a trusted path. If so, set its language mode to FullLanguage. - if (SystemPolicy.GetSystemLockdownPolicy() != SystemEnforcementMode.None) + // Check this file against any system wide enforcement policies. + SystemScriptFileEnforcement filePolicyEnforcement = SystemPolicy.GetFilePolicyEnforcement(_path, readerStream); + switch (filePolicyEnforcement) { - SystemEnforcementMode scriptSpecificPolicy = SystemPolicy.GetLockdownPolicy(_path, safeFileHandle); - if (scriptSpecificPolicy != SystemEnforcementMode.Enforce) - { - this.DefiningLanguageMode = PSLanguageMode.FullLanguage; - } - else - { - this.DefiningLanguageMode = PSLanguageMode.ConstrainedLanguage; - } - } - else - { - if (this.Context != null) - { - this.DefiningLanguageMode = this.Context.LanguageMode; - } + case SystemScriptFileEnforcement.None: + if (Context != null) + { + DefiningLanguageMode = Context.LanguageMode; + } + + break; + + case SystemScriptFileEnforcement.Allow: + DefiningLanguageMode = PSLanguageMode.FullLanguage; + break; + + case SystemScriptFileEnforcement.AllowConstrained: + DefiningLanguageMode = PSLanguageMode.ConstrainedLanguage; + break; + + case SystemScriptFileEnforcement.Block: + throw new PSSecurityException( + string.Format( + Globalization.CultureInfo.CurrentUICulture, + SecuritySupportStrings.ScriptFileBlockedBySystemPolicy, + _path)); + + default: + throw new PSSecurityException( + string.Format( + Globalization.CultureInfo.CurrentUICulture, + SecuritySupportStrings.UnknownSystemScriptFileEnforcement, + filePolicyEnforcement)); } } } diff --git a/src/System.Management.Automation/engine/GetCommandCommand.cs b/src/System.Management.Automation/engine/GetCommandCommand.cs index a1e5fa768ba..e11b3c30f8c 100644 --- a/src/System.Management.Automation/engine/GetCommandCommand.cs +++ b/src/System.Management.Automation/engine/GetCommandCommand.cs @@ -80,10 +80,7 @@ public string[] Verb set { - if (value == null) - { - value = Array.Empty(); - } + value ??= Array.Empty(); _verbs = value; _verbPatterns = null; @@ -106,10 +103,7 @@ public string[] Noun set { - if (value == null) - { - value = Array.Empty(); - } + value ??= Array.Empty(); _nouns = value; _nounPatterns = null; @@ -132,10 +126,7 @@ public string[] Module set { - if (value == null) - { - value = Array.Empty(); - } + value ??= Array.Empty(); _modules = value; _modulePatterns = null; @@ -400,10 +391,7 @@ protected override void ProcessRecord() } // Initialize the module patterns - if (_modulePatterns == null) - { - _modulePatterns = SessionStateUtilities.CreateWildcardsFromStrings(Module, WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant); - } + _modulePatterns ??= SessionStateUtilities.CreateWildcardsFromStrings(Module, WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant); switch (ParameterSetName) { @@ -686,15 +674,9 @@ private bool IsNounVerbMatch(CommandInfo command) do // false loop { - if (_verbPatterns == null) - { - _verbPatterns = SessionStateUtilities.CreateWildcardsFromStrings(Verb, WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant); - } + _verbPatterns ??= SessionStateUtilities.CreateWildcardsFromStrings(Verb, WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant); - if (_nounPatterns == null) - { - _nounPatterns = SessionStateUtilities.CreateWildcardsFromStrings(Noun, WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant); - } + _nounPatterns ??= SessionStateUtilities.CreateWildcardsFromStrings(Noun, WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant); if (!string.IsNullOrEmpty(command.ModuleName)) { @@ -1154,10 +1136,7 @@ private bool IsParameterMatch(CommandInfo commandInfo) return true; } - if (_matchedParameterNames == null) - { - _matchedParameterNames = new HashSet(StringComparer.OrdinalIgnoreCase); - } + _matchedParameterNames ??= new HashSet(StringComparer.OrdinalIgnoreCase); IEnumerable commandParameters = null; try diff --git a/src/System.Management.Automation/engine/InformationRecord.cs b/src/System.Management.Automation/engine/InformationRecord.cs index 310c34a8809..d69ad1d14b4 100644 --- a/src/System.Management.Automation/engine/InformationRecord.cs +++ b/src/System.Management.Automation/engine/InformationRecord.cs @@ -96,15 +96,13 @@ public string User { get { - if (this._user == null) - { - // domain\user on Windows, just user on Unix + // domain\user on Windows, just user on Unix + this._user ??= #if UNIX - this._user = Environment.UserName; + Environment.UserName; #else - this._user = Environment.UserDomainName + "\\" + Environment.UserName; + Environment.UserDomainName + "\\" + Environment.UserName; #endif - } return _user; } diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index 99aa639d09c..0c045818bcc 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -410,7 +410,7 @@ public SessionStateAssemblyEntry(string name) /// The cloned object. public override InitialSessionStateEntry Clone() { - SessionStateAssemblyEntry entry = new SessionStateAssemblyEntry(Name, FileName); + var entry = new SessionStateAssemblyEntry(Name, FileName); entry.SetPSSnapIn(this.PSSnapIn); entry.SetModule(this.Module); return entry; @@ -1308,45 +1308,31 @@ private static void MakeDisallowedEntriesPrivate(InitialSessionStateEntryColl } } - #region VariableHelper /// - /// A helper for adding variables to session state. - /// Experimental features can be handled here. + /// Creates an initial session state from a PSSC configuration file. /// - /// The variables to add to session state. - private void AddVariables(IEnumerable variables) + /// The path to the PSSC session configuration file. + /// InitialSessionState object. + public static InitialSessionState CreateFromSessionConfigurationFile(string path) { - Variables.Add(variables); - - // If the PSNativeCommandArgumentPassing feature is enabled, create the variable which controls the behavior - // Since the BuiltInVariables list is static, and this should be done dynamically - // we need to do this here. Also, since the defaults are different based on platform we need a - // bit more logic. - if (ExperimentalFeature.IsEnabled("PSNativeCommandArgumentPassing")) - { - NativeArgumentPassingStyle style = NativeArgumentPassingStyle.Standard; - if (Platform.IsWindows) { - style = NativeArgumentPassingStyle.Windows; - } - Variables.Add( - new SessionStateVariableEntry( - SpecialVariables.NativeArgumentPassing, - style, - RunspaceInit.NativeCommandArgumentPassingDescription, - ScopedItemOptions.None, - new ArgumentTypeConverterAttribute(typeof(NativeArgumentPassingStyle)))); - } + return CreateFromSessionConfigurationFile(path, null); } - #endregion /// /// Creates an initial session state from a PSSC configuration file. /// /// The path to the PSSC session configuration file. - /// - public static InitialSessionState CreateFromSessionConfigurationFile(string path) + /// + /// The verifier that PowerShell should call to determine if groups in the Role entry apply to the + /// target session. If you have a WindowsPrincipal for a user, for example, create a Function that + /// checks windowsPrincipal.IsInRole(). + /// + /// InitialSessionState object. + public static InitialSessionState CreateFromSessionConfigurationFile( + string path, + Func roleVerifier) { - return CreateFromSessionConfigurationFile(path, null); + return CreateFromSessionConfigurationFile(path, roleVerifier, validateFile: false); } /// @@ -1358,10 +1344,31 @@ public static InitialSessionState CreateFromSessionConfigurationFile(string path /// target session. If you have a WindowsPrincipal for a user, for example, create a Function that /// checks windowsPrincipal.IsInRole(). /// - /// - public static InitialSessionState CreateFromSessionConfigurationFile(string path, Func roleVerifier) + /// Validates the file contents for supported SessionState options. + /// InitialSessionState object. + public static InitialSessionState CreateFromSessionConfigurationFile( + string path, + Func roleVerifier, + bool validateFile) { - Remoting.DISCPowerShellConfiguration discConfiguration = new Remoting.DISCPowerShellConfiguration(path, roleVerifier); + if (path is null) + { + throw new PSArgumentNullException(nameof(path)); + } + + if (!File.Exists(path)) + { + throw new PSInvalidOperationException( + StringUtil.Format(ConsoleInfoErrorStrings.ConfigurationFileDoesNotExist, path)); + } + + if (!path.EndsWith(".pssc", StringComparison.OrdinalIgnoreCase)) + { + throw new PSInvalidOperationException( + StringUtil.Format(ConsoleInfoErrorStrings.NotConfigurationFile, path)); + } + + Remoting.DISCPowerShellConfiguration discConfiguration = new Remoting.DISCPowerShellConfiguration(path, roleVerifier, validateFile); return discConfiguration.GetInitialSessionState(null); } @@ -1444,7 +1451,7 @@ private static InitialSessionState CreateRestrictedForRemoteServer() } // Add built-in variables. - iss.AddVariables(BuiltInVariables); + iss.Variables.Add(BuiltInVariables); // wrap some commands in a proxy function to restrict their parameters foreach (KeyValuePair proxyFunction in CommandMetadata.GetRestrictedCommands(SessionCapabilities.RemoteServer)) @@ -1465,9 +1472,6 @@ private static InitialSessionState CreateRestrictedForRemoteServer() return iss; } - // Porting note: moved to Platform so we have one list to maintain - private static readonly string[] s_PSCoreFormatFileNames = Platform.FormatFileNames.ToArray(); - private static void IncludePowerShellCoreFormats(InitialSessionState iss) { string psHome = Utils.DefaultPowerShellAppBase; @@ -1477,7 +1481,7 @@ private static void IncludePowerShellCoreFormats(InitialSessionState iss) } iss.Formats.Clear(); - foreach (var coreFormat in s_PSCoreFormatFileNames) + foreach (var coreFormat in Platform.FormatFileNames) { iss.Formats.Add(new SessionStateFormatEntry(Path.Combine(psHome, coreFormat))); } @@ -1531,7 +1535,7 @@ public static InitialSessionState CreateDefault() InitialSessionState ss = new InitialSessionState(); - ss.AddVariables(BuiltInVariables); + ss.Variables.Add(BuiltInVariables); ss.Commands.Add(new SessionStateApplicationEntry("*")); ss.Commands.Add(new SessionStateScriptEntry("*")); ss.Commands.Add(BuiltInFunctions); @@ -1598,7 +1602,7 @@ public static InitialSessionState CreateDefault2() { InitialSessionState ss = new InitialSessionState(); - ss.AddVariables(BuiltInVariables); + ss.Variables.Add(BuiltInVariables); ss.Commands.Add(new SessionStateApplicationEntry("*")); ss.Commands.Add(new SessionStateScriptEntry("*")); ss.Commands.Add(BuiltInFunctions); @@ -1639,7 +1643,7 @@ public InitialSessionState Clone() { InitialSessionState ss = new InitialSessionState(); - ss.AddVariables(this.Variables.Clone()); + ss.Variables.Add(this.Variables.Clone()); ss.EnvironmentVariables.Add(this.EnvironmentVariables.Clone()); ss.Commands.Add(this.Commands.Clone()); ss.Assemblies.Add(this.Assemblies.Clone()); @@ -1697,11 +1701,6 @@ public InitialSessionState Clone() ss.DisableFormatUpdates = this.DisableFormatUpdates; - foreach (var s in this.defaultSnapins) - { - ss.defaultSnapins.Add(s); - } - foreach (var s in ImportedSnapins) { ss.ImportedSnapins.Add(s.Key, s.Value); @@ -2444,9 +2443,14 @@ private void Bind_LoadAssemblies(ExecutionContext context) // Load the assemblies and initialize the assembly cache... foreach (SessionStateAssemblyEntry ssae in Assemblies) { - if (etwEnabled) RunspaceEventSource.Log.LoadAssemblyStart(ssae.Name, ssae.FileName); - Exception error = null; - Assembly asm = context.AddAssembly(ssae.Name, ssae.FileName, out error); + if (etwEnabled) + { + RunspaceEventSource.Log.LoadAssemblyStart(ssae.Name, ssae.FileName); + } + + // Specify the source only if this is for module loading. + // The source is used for porper cleaning of the assembly cache when a module is unloaded. + Assembly asm = context.AddAssembly(ssae.Module?.Name, ssae.Name, ssae.FileName, out Exception error); if (asm == null || error != null) { @@ -2977,13 +2981,20 @@ private RunspaceOpenModuleLoadException ProcessModulesToImport( HashSet unresolvedCmdsToExpose) { RunspaceOpenModuleLoadException exceptionToReturn = null; + List processedModules = new List(); foreach (object module in moduleList) { string moduleName = module as string; if (moduleName != null) { - exceptionToReturn = ProcessOneModule(initializedRunspace, moduleName, null, path, publicCommands); + exceptionToReturn = ProcessOneModule( + initializedRunspace: initializedRunspace, + name: moduleName, + moduleInfoToLoad: null, + path: path, + publicCommands: publicCommands, + processedModules: processedModules); } else { @@ -2994,7 +3005,13 @@ private RunspaceOpenModuleLoadException ProcessModulesToImport( { // if only name is specified in the module spec, just try import the module // ie., don't take the performance overhead of calling GetModule. - exceptionToReturn = ProcessOneModule(initializedRunspace, moduleSpecification.Name, null, path, publicCommands); + exceptionToReturn = ProcessOneModule( + initializedRunspace: initializedRunspace, + name: moduleSpecification.Name, + moduleInfoToLoad: null, + path: path, + publicCommands: publicCommands, + processedModules: processedModules); } else { @@ -3002,7 +3019,13 @@ private RunspaceOpenModuleLoadException ProcessModulesToImport( if (moduleInfos != null && moduleInfos.Count > 0) { - exceptionToReturn = ProcessOneModule(initializedRunspace, moduleSpecification.Name, moduleInfos[0], path, publicCommands); + exceptionToReturn = ProcessOneModule( + initializedRunspace: initializedRunspace, + name: moduleSpecification.Name, + moduleInfoToLoad: moduleInfos[0], + path: path, + publicCommands: publicCommands, + processedModules: processedModules); } else { @@ -3051,7 +3074,11 @@ private RunspaceOpenModuleLoadException ProcessModulesToImport( string commandToMakeVisible = Utils.ParseCommandName(unresolvedCommand, out moduleName); bool found = false; - foreach (CommandInfo cmd in LookupCommands(commandToMakeVisible, moduleName, initializedRunspace.ExecutionContext)) + foreach (CommandInfo cmd in LookupCommands( + commandPattern: commandToMakeVisible, + moduleName: moduleName, + context: initializedRunspace.ExecutionContext, + processedModules: processedModules)) { if (!found) { @@ -3102,11 +3129,13 @@ private RunspaceOpenModuleLoadException ProcessModulesToImport( /// /// /// + /// /// private static IEnumerable LookupCommands( string commandPattern, string moduleName, - ExecutionContext context) + ExecutionContext context, + List processedModules) { bool isWildCardPattern = WildcardPattern.ContainsWildcardCharacters(commandPattern); var searchOptions = isWildCardPattern ? @@ -3120,7 +3149,11 @@ private static IEnumerable LookupCommands( CommandOrigin cmdOrigin = CommandOrigin.Runspace; while (true) { - foreach (CommandInfo commandInfo in context.SessionState.InvokeCommand.GetCommands(commandPattern, CommandTypes.All, searchOptions, cmdOrigin)) + foreach (CommandInfo commandInfo in context.SessionState.InvokeCommand.GetCommands( + name: commandPattern, + commandTypes: CommandTypes.All, + options: searchOptions, + commandOrigin: cmdOrigin)) { // If module name is provided then use it to restrict returned results. if (haveModuleName && !moduleName.Equals(commandInfo.ModuleName, StringComparison.OrdinalIgnoreCase)) @@ -3150,13 +3183,43 @@ private static IEnumerable LookupCommands( // Next try internal search. cmdOrigin = CommandOrigin.Internal; } + + // If the command is associated with a module, try finding the command in the imported module list. + // The SessionState function table holds only one command name, and if two or more modules contain + // a command with the same name, only one of them will appear in the function table search above. + if (!found && haveModuleName) + { + var pattern = new WildcardPattern(commandPattern); + + foreach (PSModuleInfo moduleInfo in processedModules) + { + if (moduleInfo.Name.Equals(moduleName, StringComparison.OrdinalIgnoreCase)) + { + foreach (var cmd in moduleInfo.ExportedCommands.Values) + { + if (pattern.IsMatch(cmd.Name)) + { + yield return cmd; + } + } + + break; + } + } + } } /// /// If is null, import module using . Otherwise, /// import module using /// - private RunspaceOpenModuleLoadException ProcessOneModule(Runspace initializedRunspace, string name, PSModuleInfo moduleInfoToLoad, string path, HashSet publicCommands) + private RunspaceOpenModuleLoadException ProcessOneModule( + Runspace initializedRunspace, + string name, + PSModuleInfo moduleInfoToLoad, + string path, + HashSet publicCommands, + List processedModules) { using (PowerShell pse = PowerShell.Create()) { @@ -3193,6 +3256,11 @@ private RunspaceOpenModuleLoadException ProcessOneModule(Runspace initializedRun c = new CmdletInfo("Out-Default", typeof(OutDefaultCommand), null, null, initializedRunspace.ExecutionContext); pse.AddCommand(new Command(c)); } + else + { + // For runspace init module processing, pass back the PSModuleInfo to the output pipeline. + cmd.Parameters.Add("PassThru"); + } pse.Runspace = initializedRunspace; // Module import should be run in FullLanguage mode since it is running in @@ -3201,7 +3269,10 @@ private RunspaceOpenModuleLoadException ProcessOneModule(Runspace initializedRun pse.Runspace.ExecutionContext.LanguageMode = PSLanguageMode.FullLanguage; try { - pse.Invoke(); + // For runspace init module processing, collect the imported PSModuleInfo returned in the output pipeline. + // In other cases, this collection will be empty. + Collection moduleInfos = pse.Invoke(); + processedModules.AddRange(moduleInfos); } finally { @@ -3413,105 +3484,6 @@ internal static void CreateQuestionVariable(ExecutionContext context) context.EngineSessionState.SetVariableAtScope(qv, "global", true, CommandOrigin.Internal); } - /// - /// Remove anything that would have been bound by this ISS instance. - /// At this point, it removes assemblies and cmdlet entries at the top level. - /// It also removes types and formats. - /// The other entry types - functions, variables, aliases - /// are not removed by this function. - /// - /// - internal void Unbind(ExecutionContext context) - { - lock (_syncObject) - { - SessionStateInternal ss = context.EngineSessionState; - - // Remove the assemblies from the assembly cache... - foreach (SessionStateAssemblyEntry ssae in Assemblies) - { - context.RemoveAssembly(ssae.Name); - } - - // Remove all of the commands from the top-level session state. - foreach (SessionStateCommandEntry cmd in Commands) - { - SessionStateCmdletEntry ssce = cmd as SessionStateCmdletEntry; - if (ssce != null) - { - List matches; - if (context.TopLevelSessionState.GetCmdletTable().TryGetValue(ssce.Name, out matches)) - { - // Remove the name from the list... - for (int i = matches.Count - 1; i >= 0; i--) - { - if (matches[i].ModuleName.Equals(cmd.PSSnapIn.Name)) - { - string name = matches[i].Name; - matches.RemoveAt(i); - context.TopLevelSessionState.RemoveCmdlet(name, i, /*force*/ true); - } - } - // And remove the entry if the list is now empty... - if (matches.Count == 0) - { - context.TopLevelSessionState.RemoveCmdletEntry(ssce.Name, true); - } - } - - continue; - } - } - - // Remove all of the providers from the top-level provider table. - if (_providers != null && _providers.Count > 0) - { - Dictionary> providerTable = context.TopLevelSessionState.Providers; - - foreach (SessionStateProviderEntry sspe in _providers) - { - List pl; - if (providerTable.TryGetValue(sspe.Name, out pl)) - { - Diagnostics.Assert(pl != null, "There should never be a null list of entries in the provider table"); - // For each provider with the same name... - for (int i = pl.Count - 1; i >= 0; i--) - { - ProviderInfo pi = pl[i]; - - // If it was implemented by this entry, remove it - if (pi.ImplementingType == sspe.ImplementingType) - { - RemoveAllDrivesForProvider(pi, context.TopLevelSessionState); - pl.RemoveAt(i); - } - } - - // If there are no providers left with this name, remove the key. - if (pl.Count == 0) - { - providerTable.Remove(sspe.Name); - } - } - } - } - - List formatFilesToRemove = new List(); - if (this.Formats != null) - { - formatFilesToRemove.AddRange(this.Formats.Select(static f => f.FileName)); - } - - List typeFilesToRemove = new List(); - if (this.Types != null) - { - typeFilesToRemove.AddRange(this.Types.Select(static t => t.FileName)); - } - - RemoveTypesAndFormats(context, formatFilesToRemove, typeFilesToRemove); - } - } - internal static void RemoveTypesAndFormats(ExecutionContext context, IList formatFilesToRemove, IList typeFilesToRemove) { // The formats and types tables are implemented in such a way that @@ -3630,8 +3602,7 @@ internal void UpdateTypes(ExecutionContext context, bool updateOnly) moduleName = sste.PSSnapIn.Name; } - bool unused; - context.TypeTable.Update(moduleName, sste.FileName, errors, context.AuthorizationManager, context.EngineHostInterface, out unused); + context.TypeTable.Update(moduleName, sste.FileName, errors, context.AuthorizationManager, context.EngineHostInterface, out _); } } else if (sste.TypeTable != null) @@ -3835,34 +3806,26 @@ public PSSnapInInfo ImportPSSnapIn(string name, out PSSnapInException warning) // Now actually load the snapin... PSSnapInInfo snapin = ImportPSSnapIn(newPSSnapIn, out warning); - if (snapin != null) - { - ImportedSnapins.Add(snapin.Name, snapin); - } return snapin; } internal PSSnapInInfo ImportCorePSSnapIn() { - // Load Microsoft.PowerShell.Core as a snapin + // Load Microsoft.PowerShell.Core as a snapin. PSSnapInInfo coreSnapin = PSSnapInReader.ReadCoreEngineSnapIn(); - this.defaultSnapins.Add(coreSnapin); - try - { - PSSnapInException warning; - this.ImportPSSnapIn(coreSnapin, out warning); - } - catch (PSSnapInException) - { - throw; - } - + ImportPSSnapIn(coreSnapin, out _); return coreSnapin; } internal PSSnapInInfo ImportPSSnapIn(PSSnapInInfo psSnapInInfo, out PSSnapInException warning) { + if (psSnapInInfo == null) + { + ArgumentNullException e = new ArgumentNullException(nameof(psSnapInInfo)); + throw e; + } + // See if the snapin is already loaded. If has been then there will be an entry in the // Assemblies list for it already... bool reload = true; @@ -3895,12 +3858,6 @@ internal PSSnapInInfo ImportPSSnapIn(PSSnapInInfo psSnapInInfo, out PSSnapInExce Dictionary> aliases = null; Dictionary providers = null; - if (psSnapInInfo == null) - { - ArgumentNullException e = new ArgumentNullException(nameof(psSnapInInfo)); - throw e; - } - Assembly assembly = null; string helpFile = null; @@ -3957,18 +3914,9 @@ internal PSSnapInInfo ImportPSSnapIn(PSSnapInInfo psSnapInInfo, out PSSnapInExce this.Formats.Add(formatEntry); } - SessionStateAssemblyEntry assemblyEntry = new SessionStateAssemblyEntry(psSnapInInfo.AssemblyName, psSnapInInfo.AbsoluteModulePath); - + var assemblyEntry = new SessionStateAssemblyEntry(psSnapInInfo.AssemblyName, psSnapInInfo.AbsoluteModulePath); assemblyEntry.SetPSSnapIn(psSnapInInfo); - - this.Assemblies.Add(assemblyEntry); - - // entry from types.ps1xml references a type (Microsoft.PowerShell.Commands.SecurityDescriptorCommandsBase) in this assembly - if (psSnapInInfo.Name.Equals(CoreSnapin, StringComparison.OrdinalIgnoreCase)) - { - assemblyEntry = new SessionStateAssemblyEntry("Microsoft.PowerShell.Security", null); - this.Assemblies.Add(assemblyEntry); - } + Assemblies.Add(assemblyEntry); if (cmdlets != null) { @@ -4019,37 +3967,18 @@ internal PSSnapInInfo ImportPSSnapIn(PSSnapInInfo psSnapInInfo, out PSSnapInExce } } + ImportedSnapins.Add(psSnapInInfo.Name, psSnapInInfo); return psSnapInInfo; } - internal List GetPSSnapIn(string psSnapinName) + internal PSSnapInInfo GetPSSnapIn(string psSnapinName) { - List loadedSnapins = null; - foreach (var defaultSnapin in defaultSnapins) - { - if (defaultSnapin.Name.Equals(psSnapinName, StringComparison.OrdinalIgnoreCase)) - { - if (loadedSnapins == null) - { - loadedSnapins = new List(); - } - - loadedSnapins.Add(defaultSnapin); - } - } - - PSSnapInInfo importedSnapin = null; - if (ImportedSnapins.TryGetValue(psSnapinName, out importedSnapin)) + if (ImportedSnapins.TryGetValue(psSnapinName, out PSSnapInInfo importedSnapin)) { - if (loadedSnapins == null) - { - loadedSnapins = new List(); - } - - loadedSnapins.Add(importedSnapin); + return importedSnapin; } - return loadedSnapins; + return null; } [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")] @@ -4077,20 +4006,16 @@ internal void ImportCmdletsFromAssembly(Assembly assembly, PSModuleInfo module) throw e; } - Dictionary cmdlets = null; - Dictionary> aliases = null; - Dictionary providers = null; - string assemblyPath = assembly.Location; - 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 - if (!string.IsNullOrEmpty(assembly.Location)) - { - SessionStateAssemblyEntry assemblyEntry = new SessionStateAssemblyEntry(assembly.FullName, assemblyPath); - this.Assemblies.Add(assemblyEntry); - } + PSSnapInHelpers.AnalyzePSSnapInAssembly( + assembly, + assemblyPath, + psSnapInInfo: null, + module, + out Dictionary cmdlets, + out Dictionary> aliases, + out Dictionary providers, + helpFile: out _); if (cmdlets != null) { @@ -4139,6 +4064,7 @@ internal void ImportCmdletsFromAssembly(Assembly assembly, PSModuleInfo module) #> [CmdletBinding(DefaultParameterSetName = 'ScriptInputSet')] +[OutputType([System.Management.Automation.CommandCompletion])] Param( [Parameter(ParameterSetName = 'ScriptInputSet', Mandatory = $true, Position = 0)] [string] $inputScript, @@ -4213,6 +4139,15 @@ internal static string GetClearHostFunctionText() } } +#if UNIX + internal static string GetExecFunctionText() + { + return @" +Switch-Process -WithCommand $args +"; + } +#endif + /// /// This is the default function to use for man/help. It uses /// splatting to pass in the parameters. @@ -4302,13 +4237,7 @@ .FORWARDHELPCATEGORY Cmdlet } else { $pagerCommand = 'less' - # PSNativeCommandArgumentPassing arguments should be constructed differently. - if ($EnabledExperimentalFeatures -contains 'PSNativeCommandArgumentPassing') { - $pagerArgs = '-s','-P','Page %db?B of %D:.\. Press h for help or q to quit\.' - } - else { - $pagerArgs = '-Ps""Page %db?B of %D:.\. Press h for help or q to quit\.$""' - } + $pagerArgs = '-s','-P','Page %db?B of %D:.\. Press h for help or q to quit\.' } # Respect PAGER environment variable which allows user to specify a custom pager. @@ -4348,16 +4277,7 @@ .FORWARDHELPCATEGORY Cmdlet $consoleWidth = [System.Math]::Max([System.Console]::WindowWidth, 20) if ($pagerArgs) { - # Start the pager arguments directly if the PSNativeCommandArgumentPassing feature is enabled. - # Otherwise, supply pager arguments to an application without any PowerShell parsing of the arguments. - # Leave environment variable to help user debug arguments supplied in $env:PAGER. - if ($EnabledExperimentalFeatures -contains 'PSNativeCommandArgumentPassing') { - $help | Out-String -Stream -Width ($consoleWidth - 1) | & $pagerCommand $pagerArgs - } - else { - $env:__PSPAGER_ARGS = $pagerArgs - $help | Out-String -Stream -Width ($consoleWidth - 1) | & $pagerCommand --% %__PSPAGER_ARGS% - } + $help | Out-String -Stream -Width ($consoleWidth - 1) | & $pagerCommand $pagerArgs } else { $help | Out-String -Stream -Width ($consoleWidth - 1) | & $pagerCommand @@ -4474,151 +4394,177 @@ .ForwardHelpCategory Cmdlet internal const bool DefaultWhatIfPreference = false; internal const ConfirmImpact DefaultConfirmPreference = ConfirmImpact.High; - internal static readonly SessionStateVariableEntry[] BuiltInVariables = new SessionStateVariableEntry[] - { - // Engine variables that should be precreated before running profile - // Bug fix for Win7:2202228 Engine halts if initial command fulls up variable table - // Anytime a new variable that the engine depends on to run is added, this table - // must be updated... - new SessionStateVariableEntry(SpecialVariables.LastToken, null, string.Empty), - new SessionStateVariableEntry(SpecialVariables.FirstToken, null, string.Empty), - new SessionStateVariableEntry(SpecialVariables.StackTrace, null, string.Empty), - - // Variable which controls the output rendering - new SessionStateVariableEntry( - SpecialVariables.PSStyle, - PSStyle.Instance, - RunspaceInit.PSStyleDescription, - ScopedItemOptions.None), - - // Variable which controls the encoding for piping data to a NativeCommand - new SessionStateVariableEntry( - SpecialVariables.OutputEncoding, - Utils.utf8NoBom, - RunspaceInit.OutputEncodingDescription, - ScopedItemOptions.None, - new ArgumentTypeConverterAttribute(typeof(System.Text.Encoding))), - - // Preferences - // - // NTRAID#Windows Out Of Band Releases-931461-2006/03/13 - // ArgumentTypeConverterAttribute is applied to these variables, - // but this only reaches the global variable. If these are - // redefined in script scope etc, the type conversion - // is not applicable. - // - // Variables typed to ActionPreference - new SessionStateVariableEntry( - SpecialVariables.ConfirmPreference, - DefaultConfirmPreference, - RunspaceInit.ConfirmPreferenceDescription, - ScopedItemOptions.None, - new ArgumentTypeConverterAttribute(typeof(ConfirmImpact))), - new SessionStateVariableEntry( - SpecialVariables.DebugPreference, - DefaultDebugPreference, - RunspaceInit.DebugPreferenceDescription, - ScopedItemOptions.None, - new ArgumentTypeConverterAttribute(typeof(ActionPreference))), - new SessionStateVariableEntry( - SpecialVariables.ErrorActionPreference, - DefaultErrorActionPreference, - RunspaceInit.ErrorActionPreferenceDescription, - ScopedItemOptions.None, - new ArgumentTypeConverterAttribute(typeof(ActionPreference))), - new SessionStateVariableEntry( - SpecialVariables.ProgressPreference, - DefaultProgressPreference, - RunspaceInit.ProgressPreferenceDescription, - ScopedItemOptions.None, - new ArgumentTypeConverterAttribute(typeof(ActionPreference))), - new SessionStateVariableEntry( - SpecialVariables.VerbosePreference, - DefaultVerbosePreference, - RunspaceInit.VerbosePreferenceDescription, - ScopedItemOptions.None, - new ArgumentTypeConverterAttribute(typeof(ActionPreference))), - new SessionStateVariableEntry( - SpecialVariables.WarningPreference, - DefaultWarningPreference, - RunspaceInit.WarningPreferenceDescription, - ScopedItemOptions.None, - new ArgumentTypeConverterAttribute(typeof(ActionPreference))), - new SessionStateVariableEntry( - SpecialVariables.InformationPreference, - DefaultInformationPreference, - RunspaceInit.InformationPreferenceDescription, - ScopedItemOptions.None, - new ArgumentTypeConverterAttribute(typeof(ActionPreference))), - new SessionStateVariableEntry( - SpecialVariables.ErrorView, - DefaultErrorView, - RunspaceInit.ErrorViewDescription, - ScopedItemOptions.None, - new ArgumentTypeConverterAttribute(typeof(ErrorView))), - new SessionStateVariableEntry( - SpecialVariables.NestedPromptLevel, - 0, - RunspaceInit.NestedPromptLevelDescription), - new SessionStateVariableEntry( - SpecialVariables.WhatIfPreference, - DefaultWhatIfPreference, - RunspaceInit.WhatIfPreferenceDescription), - new SessionStateVariableEntry( - FormatEnumerationLimit, - DefaultFormatEnumerationLimit, - RunspaceInit.FormatEnumerationLimitDescription), - - // variable for PSEmailServer - new SessionStateVariableEntry( - SpecialVariables.PSEmailServer, - string.Empty, - RunspaceInit.PSEmailServerDescription), - - // Start: Variables which control remoting behavior - new SessionStateVariableEntry( - Microsoft.PowerShell.Commands.PSRemotingBaseCmdlet.DEFAULT_SESSION_OPTION, - new System.Management.Automation.Remoting.PSSessionOption(), - RemotingErrorIdStrings.PSDefaultSessionOptionDescription, - ScopedItemOptions.None), - new SessionStateVariableEntry( - SpecialVariables.PSSessionConfigurationName, - "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", - RemotingErrorIdStrings.PSSessionConfigurationName, - ScopedItemOptions.None), - new SessionStateVariableEntry( - SpecialVariables.PSSessionApplicationName, - "wsman", - RemotingErrorIdStrings.PSSessionAppName, - ScopedItemOptions.None), - // End: Variables which control remoting behavior - - #region Platform - new SessionStateVariableEntry( - SpecialVariables.IsLinux, - Platform.IsLinux, - string.Empty, - ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope), - - new SessionStateVariableEntry( - SpecialVariables.IsMacOS, - Platform.IsMacOS, - string.Empty, - ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope), - - new SessionStateVariableEntry( - SpecialVariables.IsWindows, - Platform.IsWindows, - string.Empty, - ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope), - - new SessionStateVariableEntry( - SpecialVariables.IsCoreCLR, - Platform.IsCoreCLR, - string.Empty, - ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope), - #endregion - }; + static InitialSessionState() + { + var builtinVariables = new List() + { + // Engine variables that should be precreated before running profile + // Bug fix for Win7:2202228 Engine halts if initial command fulls up variable table + // Anytime a new variable that the engine depends on to run is added, this table + // must be updated... + new SessionStateVariableEntry(SpecialVariables.LastToken, null, string.Empty), + new SessionStateVariableEntry(SpecialVariables.FirstToken, null, string.Empty), + new SessionStateVariableEntry(SpecialVariables.StackTrace, null, string.Empty), + + // Variable which controls the output rendering + new SessionStateVariableEntry( + SpecialVariables.PSStyle, + PSStyle.Instance, + RunspaceInit.PSStyleDescription, + ScopedItemOptions.Constant), + + // Variable which controls the encoding for piping data to a NativeCommand + new SessionStateVariableEntry( + SpecialVariables.OutputEncoding, + Utils.utf8NoBom, + RunspaceInit.OutputEncodingDescription, + ScopedItemOptions.None, + new ArgumentTypeConverterAttribute(typeof(System.Text.Encoding))), + + // Preferences + // + // NTRAID#Windows Out Of Band Releases-931461-2006/03/13 + // ArgumentTypeConverterAttribute is applied to these variables, + // but this only reaches the global variable. If these are + // redefined in script scope etc, the type conversion + // is not applicable. + // + // Variables typed to ActionPreference + new SessionStateVariableEntry( + SpecialVariables.ConfirmPreference, + DefaultConfirmPreference, + RunspaceInit.ConfirmPreferenceDescription, + ScopedItemOptions.None, + new ArgumentTypeConverterAttribute(typeof(ConfirmImpact))), + new SessionStateVariableEntry( + SpecialVariables.DebugPreference, + DefaultDebugPreference, + RunspaceInit.DebugPreferenceDescription, + ScopedItemOptions.None, + new ArgumentTypeConverterAttribute(typeof(ActionPreference))), + new SessionStateVariableEntry( + SpecialVariables.ErrorActionPreference, + DefaultErrorActionPreference, + RunspaceInit.ErrorActionPreferenceDescription, + ScopedItemOptions.None, + new ArgumentTypeConverterAttribute(typeof(ActionPreference))), + new SessionStateVariableEntry( + SpecialVariables.ProgressPreference, + DefaultProgressPreference, + RunspaceInit.ProgressPreferenceDescription, + ScopedItemOptions.None, + new ArgumentTypeConverterAttribute(typeof(ActionPreference))), + new SessionStateVariableEntry( + SpecialVariables.VerbosePreference, + DefaultVerbosePreference, + RunspaceInit.VerbosePreferenceDescription, + ScopedItemOptions.None, + new ArgumentTypeConverterAttribute(typeof(ActionPreference))), + new SessionStateVariableEntry( + SpecialVariables.WarningPreference, + DefaultWarningPreference, + RunspaceInit.WarningPreferenceDescription, + ScopedItemOptions.None, + new ArgumentTypeConverterAttribute(typeof(ActionPreference))), + new SessionStateVariableEntry( + SpecialVariables.InformationPreference, + DefaultInformationPreference, + RunspaceInit.InformationPreferenceDescription, + ScopedItemOptions.None, + new ArgumentTypeConverterAttribute(typeof(ActionPreference))), + new SessionStateVariableEntry( + SpecialVariables.ErrorView, + DefaultErrorView, + RunspaceInit.ErrorViewDescription, + ScopedItemOptions.None, + new ArgumentTypeConverterAttribute(typeof(ErrorView))), + new SessionStateVariableEntry( + SpecialVariables.NestedPromptLevel, + 0, + RunspaceInit.NestedPromptLevelDescription), + new SessionStateVariableEntry( + SpecialVariables.WhatIfPreference, + DefaultWhatIfPreference, + RunspaceInit.WhatIfPreferenceDescription), + new SessionStateVariableEntry( + FormatEnumerationLimit, + DefaultFormatEnumerationLimit, + RunspaceInit.FormatEnumerationLimitDescription), + + // variable for PSEmailServer + new SessionStateVariableEntry( + SpecialVariables.PSEmailServer, + string.Empty, + RunspaceInit.PSEmailServerDescription), + + // Start: Variables which control remoting behavior + new SessionStateVariableEntry( + Microsoft.PowerShell.Commands.PSRemotingBaseCmdlet.DEFAULT_SESSION_OPTION, + new System.Management.Automation.Remoting.PSSessionOption(), + RemotingErrorIdStrings.PSDefaultSessionOptionDescription, + ScopedItemOptions.None), + new SessionStateVariableEntry( + SpecialVariables.PSSessionConfigurationName, + "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", + RemotingErrorIdStrings.PSSessionConfigurationName, + ScopedItemOptions.None), + new SessionStateVariableEntry( + SpecialVariables.PSSessionApplicationName, + "wsman", + RemotingErrorIdStrings.PSSessionAppName, + ScopedItemOptions.None), + // End: Variables which control remoting behavior + + #region Platform + new SessionStateVariableEntry( + SpecialVariables.IsLinux, + Platform.IsLinux, + string.Empty, + ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope), + + new SessionStateVariableEntry( + SpecialVariables.IsMacOS, + Platform.IsMacOS, + string.Empty, + ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope), + + new SessionStateVariableEntry( + SpecialVariables.IsWindows, + Platform.IsWindows, + string.Empty, + ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope), + + new SessionStateVariableEntry( + SpecialVariables.IsCoreCLR, + Platform.IsCoreCLR, + string.Empty, + ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope), + #endregion + }; + + if (ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandErrorActionPreferenceFeatureName)) + { + builtinVariables.Add( + new SessionStateVariableEntry( + SpecialVariables.PSNativeCommandUseErrorActionPreference, + value: false, + RunspaceInit.PSNativeCommandUseErrorActionPreferenceDescription, + ScopedItemOptions.None, + new ArgumentTypeConverterAttribute(typeof(bool)))); + } + + builtinVariables.Add( + new SessionStateVariableEntry( + SpecialVariables.NativeArgumentPassing, + Platform.IsWindows ? NativeArgumentPassingStyle.Windows : NativeArgumentPassingStyle.Standard, + RunspaceInit.NativeCommandArgumentPassingDescription, + ScopedItemOptions.None, + new ArgumentTypeConverterAttribute(typeof(NativeArgumentPassingStyle)))); + + BuiltInVariables = builtinVariables.ToArray(); + } + + internal static readonly SessionStateVariableEntry[] BuiltInVariables; /// /// Returns a new array of alias entries everytime it's called. This @@ -4639,7 +4585,7 @@ internal static SessionStateAliasEntry[] BuiltInAliases const ScopedItemOptions ReadOnly_AllScope = ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope; const ScopedItemOptions ReadOnly = ScopedItemOptions.ReadOnly; - return new SessionStateAliasEntry[] { + var builtInAliases = new List { new SessionStateAliasEntry("foreach", "ForEach-Object", string.Empty, ReadOnly_AllScope), new SessionStateAliasEntry("%", "ForEach-Object", string.Empty, ReadOnly_AllScope), new SessionStateAliasEntry("where", "Where-Object", string.Empty, ReadOnly_AllScope), @@ -4806,6 +4752,8 @@ internal static SessionStateAliasEntry[] BuiltInAliases // - do not use AllScope - this causes errors in profiles that set this somewhat commonly used alias. new SessionStateAliasEntry("sls", "Select-String"), }; + + return builtInAliases.ToArray(); } } @@ -4833,6 +4781,10 @@ internal static SessionStateAliasEntry[] BuiltInAliases SessionStateFunctionEntry.GetDelayParsedFunctionEntry("help", GetHelpPagingFunctionText(), isProductCode: true, languageMode: systemLanguageMode), SessionStateFunctionEntry.GetDelayParsedFunctionEntry("prompt", DefaultPromptFunctionText, isProductCode: true, languageMode: systemLanguageMode), +#if UNIX + SessionStateFunctionEntry.GetDelayParsedFunctionEntry("exec", GetExecFunctionText(), isProductCode: true, languageMode: systemLanguageMode), +#endif + // Functions that require full language mode and are trusted SessionStateFunctionEntry.GetDelayParsedFunctionEntry("Clear-Host", GetClearHostFunctionText(), isProductCode: true, languageMode: PSLanguageMode.FullLanguage), SessionStateFunctionEntry.GetDelayParsedFunctionEntry("TabExpansion2", s_tabExpansionFunctionText, isProductCode: true, languageMode: PSLanguageMode.FullLanguage), @@ -4891,7 +4843,6 @@ internal static void RemoveAllDrivesForProvider(ProviderInfo pi, SessionStateInt internal static readonly string CoreSnapin = "Microsoft.PowerShell.Core"; internal static readonly string CoreModule = "Microsoft.PowerShell.Core"; - internal Collection defaultSnapins = new Collection(); // The list of engine modules to create warnings when you try to remove them internal static readonly HashSet EngineModules = new HashSet(StringComparer.OrdinalIgnoreCase) @@ -5375,7 +5326,6 @@ private static void InitializeCoreCmdletsAndProviders( { "Enable-PSSessionConfiguration", new SessionStateCmdletEntry("Enable-PSSessionConfiguration", typeof(EnablePSSessionConfigurationCommand), helpFile) }, { "Get-PSSessionCapability", new SessionStateCmdletEntry("Get-PSSessionCapability", typeof(GetPSSessionCapabilityCommand), helpFile) }, { "Get-PSSessionConfiguration", new SessionStateCmdletEntry("Get-PSSessionConfiguration", typeof(GetPSSessionConfigurationCommand), helpFile) }, - { "New-PSSessionConfigurationFile", new SessionStateCmdletEntry("New-PSSessionConfigurationFile", typeof(NewPSSessionConfigurationFileCommand), helpFile) }, { "Receive-PSSession", new SessionStateCmdletEntry("Receive-PSSession", typeof(ReceivePSSessionCommand), helpFile) }, { "Register-PSSessionConfiguration", new SessionStateCmdletEntry("Register-PSSessionConfiguration", typeof(RegisterPSSessionConfigurationCommand), helpFile) }, { "Unregister-PSSessionConfiguration", new SessionStateCmdletEntry("Unregister-PSSessionConfiguration", typeof(UnregisterPSSessionConfigurationCommand), helpFile) }, @@ -5407,6 +5357,7 @@ private static void InitializeCoreCmdletsAndProviders( { "New-ModuleManifest", new SessionStateCmdletEntry("New-ModuleManifest", typeof(NewModuleManifestCommand), helpFile) }, { "New-PSRoleCapabilityFile", new SessionStateCmdletEntry("New-PSRoleCapabilityFile", typeof(NewPSRoleCapabilityFileCommand), helpFile) }, { "New-PSSession", new SessionStateCmdletEntry("New-PSSession", typeof(NewPSSessionCommand), helpFile) }, + { "New-PSSessionConfigurationFile", new SessionStateCmdletEntry("New-PSSessionConfigurationFile", typeof(NewPSSessionConfigurationFileCommand), helpFile) }, { "New-PSSessionOption", new SessionStateCmdletEntry("New-PSSessionOption", typeof(NewPSSessionOptionCommand), helpFile) }, { "New-PSTransportOption", new SessionStateCmdletEntry("New-PSTransportOption", typeof(NewPSTransportOptionCommand), helpFile) }, { "Out-Default", new SessionStateCmdletEntry("Out-Default", typeof(OutDefaultCommand), helpFile) }, @@ -5444,6 +5395,10 @@ private static void InitializeCoreCmdletsAndProviders( cmdlets.Add("Get-PSSubsystem", new SessionStateCmdletEntry("Get-PSSubsystem", typeof(Subsystem.GetPSSubsystemCommand), helpFile)); } +#if UNIX + cmdlets.Add("Switch-Process", new SessionStateCmdletEntry("Switch-Process", typeof(SwitchProcessCommand), helpFile)); +#endif + foreach (var val in cmdlets.Values) { val.SetPSSnapIn(psSnapInInfo); diff --git a/src/System.Management.Automation/engine/InternalCommands.cs b/src/System.Management.Automation/engine/InternalCommands.cs index 8d628aea660..f1c5f57c88f 100644 --- a/src/System.Management.Automation/engine/InternalCommands.cs +++ b/src/System.Management.Automation/engine/InternalCommands.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Dynamic; using System.Globalization; -using System.Linq; using System.Linq.Expressions; using System.Management.Automation; using System.Management.Automation.Internal; @@ -15,8 +14,10 @@ using System.Runtime.CompilerServices; using System.Text; using System.Threading; + using CommonParamSet = System.Management.Automation.Internal.CommonParameters; using Dbg = System.Management.Automation.Diagnostics; +using NotNullWhen = System.Diagnostics.CodeAnalysis.NotNullWhenAttribute; namespace Microsoft.PowerShell.Commands { @@ -381,17 +382,6 @@ public void Dispose() private Exception _taskCollectionException; private string _currentLocationPath; - // List of Foreach-Object command names and aliases. - // TODO: Look into using SessionState.Internal.GetAliasTable() to find all user created aliases. - // But update Alias command logic to maintain reverse table that lists all aliases mapping - // to a single command definition, for performance. - private static readonly string[] forEachNames = new string[] - { - "ForEach-Object", - "foreach", - "%" - }; - private void InitParallelParameterSet() { // The following common parameters are not (yet) supported in this parameter set. @@ -422,15 +412,14 @@ private void InitParallelParameterSet() _usingValuesMap = ScriptBlockToPowerShellConverter.GetUsingValuesForEachParallel( scriptBlock: Parallel, isTrustedInput: allowUsingExpression, - context: this.Context, - foreachNames: forEachNames); + context: this.Context); // Validate using values map, which is a map of '$using:' variables referenced in the script. // Script block variables are not allowed since their behavior is undefined outside the runspace // in which they were created. foreach (object item in _usingValuesMap.Values) { - if (item is ScriptBlock) + if (item is ScriptBlock or PSObject { BaseObject: ScriptBlock }) { ThrowTerminatingError( new ErrorRecord( @@ -930,17 +919,14 @@ private void ProcessScriptBlockParameterSet() // because it allows you to parameterize a command - for example you might allow // for actions before and after the main processing script. They could be null // by default and therefore ignored then filled in later... - if (_scripts[i] != null) - { - _scripts[i].InvokeUsingCmdlet( - contextCmdlet: this, - useLocalScope: false, - errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, - dollarUnder: InputObject, - input: new object[] { InputObject }, - scriptThis: AutomationNull.Value, - args: Array.Empty()); - } + _scripts[i]?.InvokeUsingCmdlet( + contextCmdlet: this, + useLocalScope: false, + errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, + dollarUnder: InputObject, + input: new object[] { InputObject }, + scriptThis: AutomationNull.Value, + args: Array.Empty()); } } @@ -1550,7 +1536,7 @@ public SwitchParameter LT } /// - /// Gets -sets case sensitive binary operator -clt. + /// Gets or sets case sensitive binary operator -clt. /// [Parameter(Mandatory = true, ParameterSetName = "CaseSensitiveLessThanSet")] public SwitchParameter CLT @@ -2647,46 +2633,19 @@ public SwitchParameter Off private SwitchParameter _off; /// - /// To make it easier to specify a version, we add some conversions that wouldn't happen otherwise: - /// * A simple integer, i.e. 2 - /// * A string without a dot, i.e. "2" - /// * The string 'latest', which we interpret to be the current version of PowerShell. + /// Handle 'latest', which we interpret to be the current version of PowerShell. /// - private sealed class ArgumentToVersionTransformationAttribute : ArgumentTransformationAttribute + private sealed class ArgumentToPSVersionTransformationAttribute : ArgumentToVersionTransformationAttribute { - public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) + protected override bool TryConvertFromString(string versionString, [NotNullWhen(true)] out Version version) { - object version = PSObject.Base(inputData); - - string versionStr = version as string; - if (versionStr != null) + if (string.Equals("latest", versionString, StringComparison.OrdinalIgnoreCase)) { - if (versionStr.Equals("latest", StringComparison.OrdinalIgnoreCase)) - { - return PSVersionInfo.PSVersion; - } - - if (versionStr.Contains('.')) - { - // If the string contains a '.', let the Version constructor handle the conversion. - return inputData; - } - } - - if (version is double) - { - // The conversion to int below is wrong, but the usual conversions will turn - // the double into a string, so just return the original object. - return inputData; - } - - int majorVersion; - if (LanguagePrimitives.TryConvertTo(version, out majorVersion)) - { - return new Version(majorVersion, 0); + version = PSVersionInfo.PSVersion; + return true; } - return inputData; + return base.TryConvertFromString(versionString, out version); } } @@ -2711,7 +2670,7 @@ protected override void Validate(object arguments, EngineIntrinsics engineIntrin /// Gets or sets strict mode in the current scope. /// [Parameter(ParameterSetName = "Version", Mandatory = true)] - [ArgumentToVersionTransformation] + [ArgumentToPSVersionTransformation] [ValidateVersion] [Alias("v")] public Version Version diff --git a/src/System.Management.Automation/engine/LanguagePrimitives.cs b/src/System.Management.Automation/engine/LanguagePrimitives.cs index 3176fbbead1..283ea28dd96 100644 --- a/src/System.Management.Automation/engine/LanguagePrimitives.cs +++ b/src/System.Management.Automation/engine/LanguagePrimitives.cs @@ -310,9 +310,11 @@ public static class LanguagePrimitives internal static void CreateMemberNotFoundError(PSObject pso, DictionaryEntry property, Type resultType) { - string availableProperties = GetAvailableProperties(pso); + string settableProperties = GetSettableProperties(pso); - string message = StringUtil.Format(ExtendedTypeSystem.PropertyNotFound, property.Key.ToString(), resultType.FullName, availableProperties); + string message = settableProperties == string.Empty + ? StringUtil.Format(ExtendedTypeSystem.NoSettableProperty, property.Key.ToString(), resultType.FullName) + : StringUtil.Format(ExtendedTypeSystem.PropertyNotFound, property.Key.ToString(), resultType.FullName, settableProperties); typeConversion.WriteLine("Issuing an error message about not being able to create an object from hashtable."); throw new InvalidOperationException(message); @@ -632,10 +634,7 @@ public static bool Equals(object first, object second, bool ignoreCase, IFormatP // If second can be converted to the type of the first, it does so and returns first.Equals(secondConverted) // Otherwise false is returned - if (formatProvider == null) - { - formatProvider = CultureInfo.InvariantCulture; - } + formatProvider ??= CultureInfo.InvariantCulture; if (!(formatProvider is CultureInfo culture)) { @@ -779,10 +778,7 @@ public static int Compare(object first, object second, bool ignoreCase) /// public static int Compare(object first, object second, bool ignoreCase, IFormatProvider formatProvider) { - if (formatProvider == null) - { - formatProvider = CultureInfo.InvariantCulture; - } + formatProvider ??= CultureInfo.InvariantCulture; if (!(formatProvider is CultureInfo culture)) { @@ -899,10 +895,7 @@ public static bool TryCompare(object first, object second, bool ignoreCase, out public static bool TryCompare(object first, object second, bool ignoreCase, IFormatProvider formatProvider, out int result) { result = 0; - if (formatProvider == null) - { - formatProvider = CultureInfo.InvariantCulture; - } + formatProvider ??= CultureInfo.InvariantCulture; if (formatProvider is not CultureInfo culture) { @@ -4673,7 +4666,7 @@ internal static PSObject SetObjectProperties(object o, IDictionary properties, T Type propType; if (TypeResolver.TryResolveType(property.TypeNameOfValue, out propType)) { - if (formatProvider == null) { formatProvider = CultureInfo.InvariantCulture; } + formatProvider ??= CultureInfo.InvariantCulture; try { @@ -4735,18 +4728,23 @@ internal static PSObject SetObjectProperties(object o, IDictionary properties, T return pso; } - private static string GetAvailableProperties(PSObject pso) + private static string GetSettableProperties(PSObject pso) { + if (pso is null || pso.Properties is null) + { + return string.Empty; + } + StringBuilder availableProperties = new StringBuilder(); bool first = true; - if (pso != null && pso.Properties != null) + foreach (PSPropertyInfo p in pso.Properties) { - foreach (PSPropertyInfo p in pso.Properties) + if (p.IsSettable) { if (!first) { - availableProperties.Append(" , "); + availableProperties.Append(", "); } availableProperties.Append("[" + p.Name + " <" + p.TypeNameOfValue + ">]"); @@ -5736,10 +5734,7 @@ internal static IConversionData FigureConversion(Type fromType, Type toType) } } - if (converter == null) - { - converter = FigurePropertyConversion(fromType, toType, ref rank); - } + converter ??= FigurePropertyConversion(fromType, toType, ref rank); if (TypeConverterPossiblyExists(fromType) || TypeConverterPossiblyExists(toType) || (converter != null && valueDependentConversion != null)) diff --git a/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs b/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs index 2cbb30bcb00..48836d6cc96 100644 --- a/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs +++ b/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs @@ -548,32 +548,15 @@ private void ImportModule_ViaLocalModuleInfo(ImportModuleOptions importModuleOpt private void ImportModule_ViaAssembly(ImportModuleOptions importModuleOptions, Assembly suppliedAssembly) { bool moduleLoaded = false; + string moduleName = "dynamic_code_module_" + suppliedAssembly.FullName; + // Loop through Module Cache to ensure that the module is not already imported. - if (suppliedAssembly != null && Context.Modules.ModuleTable != null) + foreach (KeyValuePair pair in Context.Modules.ModuleTable) { - foreach (KeyValuePair pair in Context.Modules.ModuleTable) + if (pair.Value.Path == string.Empty) { - // if the module in the moduleTable is an assembly module without path, the moduleName is the key. - string moduleName = "dynamic_code_module_" + suppliedAssembly; - if (pair.Value.Path == string.Empty) - { - if (pair.Key.Equals(moduleName, StringComparison.OrdinalIgnoreCase)) - { - moduleLoaded = true; - if (BasePassThru) - { - WriteObject(pair.Value); - } - - break; - } - else - { - continue; - } - } - - if (pair.Value.Path.Equals(suppliedAssembly.Location, StringComparison.OrdinalIgnoreCase)) + // If the module in the moduleTable is an assembly module without path, the moduleName is the key. + if (pair.Key.Equals(moduleName, StringComparison.OrdinalIgnoreCase)) { moduleLoaded = true; if (BasePassThru) @@ -583,12 +566,26 @@ private void ImportModule_ViaAssembly(ImportModuleOptions importModuleOptions, A break; } + + continue; + } + + if (pair.Value.Path.Equals(suppliedAssembly.Location, StringComparison.OrdinalIgnoreCase)) + { + moduleLoaded = true; + if (BasePassThru) + { + WriteObject(pair.Value); + } + + break; } } if (!moduleLoaded) { PSModuleInfo module = LoadBinaryModule( + parentModule: null, moduleName: null, fileName: null, suppliedAssembly, @@ -596,15 +593,13 @@ private void ImportModule_ViaAssembly(ImportModuleOptions importModuleOptions, A ss: null, importModuleOptions, ManifestProcessingFlags.LoadElements | ManifestProcessingFlags.WriteErrors | ManifestProcessingFlags.NullOnFirstError, - this.BasePrefix, - loadTypes: false, - loadFormats: false, + BasePrefix, out bool found); - if (found && module != null) + if (found && module is not null) { // Add it to all module tables ... - AddModuleToModuleTables(this.Context, this.TargetSessionState.Internal, module); + AddModuleToModuleTables(Context, TargetSessionState.Internal, module); if (BasePassThru) { WriteObject(module); @@ -624,7 +619,7 @@ private PSModuleInfo ImportModule_LocallyViaName_WithTelemetry(ImportModuleOptio // avoid double reporting for WinCompat modules that go through CommandDiscovery\AutoloadSpecifiedModule if (!foundModule.IsWindowsPowerShellCompatModule) { - ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, foundModule.Name); + ApplicationInsightsTelemetry.SendModuleTelemetryMetric(TelemetryType.ModuleLoad, foundModule.Name, foundModule.Version?.ToString()); #if LEGACYTELEMETRY TelemetryAPI.ReportModuleLoad(foundModule); #endif @@ -636,6 +631,8 @@ private PSModuleInfo ImportModule_LocallyViaName_WithTelemetry(ImportModuleOptio private PSModuleInfo ImportModule_LocallyViaName(ImportModuleOptions importModuleOptions, string name) { + bool shallWriteError = !importModuleOptions.SkipSystem32ModulesAndSuppressError; + try { bool found = false; @@ -664,21 +661,18 @@ private PSModuleInfo ImportModule_LocallyViaName(ImportModuleOptions importModul } } - if (rootedPath == null) + // If null check for full-qualified paths - either absolute or relative + rootedPath ??= ResolveRootedFilePath(name, this.Context); + + bool alreadyLoaded = false; + var manifestProcessingFlags = ManifestProcessingFlags.LoadElements | ManifestProcessingFlags.NullOnFirstError; + if (shallWriteError) { - // Check for full-qualified paths - either absolute or relative - rootedPath = ResolveRootedFilePath(name, this.Context); + manifestProcessingFlags |= ManifestProcessingFlags.WriteErrors; } - bool alreadyLoaded = false; if (!string.IsNullOrEmpty(rootedPath)) { - // TODO/FIXME: use IsModuleAlreadyLoaded to get consistent behavior - // TODO/FIXME: (for example checking ModuleType != Manifest below seems incorrect - cdxml modules also declare their own version) - // PSModuleInfo alreadyLoadedModule = null; - // TryGetFromModuleTable(rootedPath, out alreadyLoadedModule); - // if (!BaseForce && IsModuleAlreadyLoaded(alreadyLoadedModule)) - // If the module has already been loaded, just emit it and continue... if (!BaseForce && TryGetFromModuleTable(rootedPath, out PSModuleInfo module)) { @@ -725,9 +719,14 @@ private PSModuleInfo ImportModule_LocallyViaName(ImportModuleOptions importModul RemoveModule(moduleToRemove); } - foundModule = LoadModule(rootedPath, null, this.BasePrefix, null, ref importModuleOptions, - ManifestProcessingFlags.LoadElements | ManifestProcessingFlags.WriteErrors | ManifestProcessingFlags.NullOnFirstError, - out found); + foundModule = LoadModule( + fileName: rootedPath, + moduleBase: null, + prefix: BasePrefix, + ss: null, /*SessionState*/ + ref importModuleOptions, + manifestProcessingFlags, + out found); } else if (Directory.Exists(rootedPath)) { @@ -738,21 +737,24 @@ private PSModuleInfo ImportModule_LocallyViaName(ImportModuleOptions importModul } // Load the latest valid version if it is a multi-version module directory - foundModule = LoadUsingMultiVersionModuleBase(rootedPath, - ManifestProcessingFlags.LoadElements | - ManifestProcessingFlags.WriteErrors | - ManifestProcessingFlags.NullOnFirstError, - importModuleOptions, out found); + foundModule = LoadUsingMultiVersionModuleBase(rootedPath, manifestProcessingFlags, importModuleOptions, out found); if (!found) { // If the path is a directory, double up the end of the string // then try to load that using extensions... rootedPath = Path.Combine(rootedPath, Path.GetFileName(rootedPath)); - foundModule = LoadUsingExtensions(null, rootedPath, rootedPath, null, null, this.BasePrefix, /*SessionState*/ null, - importModuleOptions, - ManifestProcessingFlags.LoadElements | ManifestProcessingFlags.WriteErrors | ManifestProcessingFlags.NullOnFirstError, - out found); + foundModule = LoadUsingExtensions( + parentModule: null, + moduleName: rootedPath, + fileBaseName: rootedPath, + extension: null, + moduleBase: null, + prefix: BasePrefix, + ss: null, /*SessionState*/ + importModuleOptions, + manifestProcessingFlags, + out found); } } } @@ -762,7 +764,7 @@ private PSModuleInfo ImportModule_LocallyViaName(ImportModuleOptions importModul // Check if module could be a snapin. This was the case for PowerShell version 2 engine modules. if (InitialSessionState.IsEngineModule(name)) { - PSSnapInInfo snapin = ModuleCmdletBase.GetEngineSnapIn(Context, name); + PSSnapInInfo snapin = Context.CurrentRunspace.InitialSessionState.GetPSSnapIn(name); // Return the command if we found a module if (snapin != null) @@ -786,16 +788,28 @@ private PSModuleInfo ImportModule_LocallyViaName(ImportModuleOptions importModul // If there is no extension, we'll have to search using the extensions if (!string.IsNullOrEmpty(Path.GetExtension(name))) { - foundModule = LoadModule(name, null, this.BasePrefix, null, ref importModuleOptions, - ManifestProcessingFlags.LoadElements | ManifestProcessingFlags.WriteErrors | ManifestProcessingFlags.NullOnFirstError, - out found); + foundModule = LoadModule( + fileName: name, + moduleBase: null, + prefix: BasePrefix, + ss: null, /*SessionState*/ + ref importModuleOptions, + manifestProcessingFlags, + out found); } else { - foundModule = LoadUsingExtensions(null, name, name, null, null, this.BasePrefix, /*SessionState*/ null, - importModuleOptions, - ManifestProcessingFlags.LoadElements | ManifestProcessingFlags.WriteErrors | ManifestProcessingFlags.NullOnFirstError, - out found); + foundModule = LoadUsingExtensions( + parentModule: null, + moduleName: name, + fileBaseName: name, + extension: null, + moduleBase: null, + prefix: BasePrefix, + ss: null, /*SessionState*/ + importModuleOptions, + manifestProcessingFlags, + out found); } } else @@ -807,14 +821,17 @@ private PSModuleInfo ImportModule_LocallyViaName(ImportModuleOptions importModul this.AddToAppDomainLevelCache = true; } - found = LoadUsingModulePath(found, modulePath, name, /* SessionState*/ null, - importModuleOptions, - ManifestProcessingFlags.LoadElements | ManifestProcessingFlags.WriteErrors | ManifestProcessingFlags.NullOnFirstError, - out foundModule); + found = LoadUsingModulePath( + modulePath, + name, + ss: null, /* SessionState*/ + importModuleOptions, + manifestProcessingFlags, + out foundModule); } } - if (!found) + if (!found && shallWriteError) { ErrorRecord er = null; string message = null; @@ -856,8 +873,10 @@ private PSModuleInfo ImportModule_LocallyViaName(ImportModuleOptions importModul } catch (PSInvalidOperationException e) { - ErrorRecord er = new ErrorRecord(e.ErrorRecord, e); - WriteError(er); + if (shallWriteError) + { + WriteError(new ErrorRecord(e.ErrorRecord, e)); + } } return null; @@ -874,7 +893,7 @@ private PSModuleInfo ImportModule_LocallyViaFQName(ImportModuleOptions importMod if (foundModule != null) { - ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, foundModule.Name); + ApplicationInsightsTelemetry.SendModuleTelemetryMetric(TelemetryType.ModuleLoad, foundModule.Name, foundModule.Version?.ToString()); SetModuleBaseForEngineModules(foundModule.Name, this.Context); } @@ -916,7 +935,7 @@ private IList ImportModule_RemotelyViaPsrpSession( // Send telemetry on the imported modules foreach (PSModuleInfo moduleInfo in remotelyImportedModules) { - ApplicationInsightsTelemetry.SendTelemetryMetric(usingWinCompat ? TelemetryType.WinCompatModuleLoad : TelemetryType.ModuleLoad, moduleInfo.Name); + ApplicationInsightsTelemetry.SendModuleTelemetryMetric(usingWinCompat ? TelemetryType.WinCompatModuleLoad : TelemetryType.ModuleLoad, moduleInfo.Name, moduleInfo.Version?.ToString()); } return remotelyImportedModules; @@ -1347,20 +1366,32 @@ private void ImportModule_RemotelyViaCimSession( foreach (RemoteDiscoveryHelper.CimModule remoteCimModule in remotePsCimModules) { ImportModule_RemotelyViaCimModuleData(importModuleOptions, remoteCimModule, cimSession); - ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, remoteCimModule.ModuleName); + // we don't know the version of the module + ApplicationInsightsTelemetry.SendModuleTelemetryMetric(TelemetryType.ModuleLoad, remoteCimModule.ModuleName); } } private bool IsPs1xmlFileHelper_IsPresentInEntries(RemoteDiscoveryHelper.CimModuleFile cimModuleFile, IEnumerable manifestEntries) { - if (manifestEntries.Any(s => s.EndsWith(cimModuleFile.FileName, StringComparison.OrdinalIgnoreCase))) + const string ps1xmlExt = ".ps1xml"; + string fileName = cimModuleFile.FileName; + + foreach (string entry in manifestEntries) { - return true; + if (entry.EndsWith(fileName, StringComparison.OrdinalIgnoreCase)) + { + return true; + } } - if (manifestEntries.Any(s => FixupFileName(string.Empty, s, ".ps1xml", isImportingModule: true).EndsWith(cimModuleFile.FileName, StringComparison.OrdinalIgnoreCase))) + foreach (string entry in manifestEntries) { - return true; + string tempName = entry.EndsWith(ps1xmlExt, StringComparison.OrdinalIgnoreCase) ? entry : entry + ps1xmlExt; + string resolvedPath = ResolveRootedFilePath(tempName, Context); + if (resolvedPath is not null && resolvedPath.EndsWith(fileName, StringComparison.OrdinalIgnoreCase)) + { + return true; + } } return false; @@ -1379,10 +1410,7 @@ private bool IsPs1xmlFileHelper(RemoteDiscoveryHelper.CimModuleFile cimModuleFil goodEntries = new List(); } - if (goodEntries == null) - { - goodEntries = new List(); - } + goodEntries ??= new List(); List badEntries; if (!this.GetListOfStringsFromData(manifestData, null, badKey, 0, out badEntries)) @@ -1390,10 +1418,7 @@ private bool IsPs1xmlFileHelper(RemoteDiscoveryHelper.CimModuleFile cimModuleFil badEntries = new List(); } - if (badEntries == null) - { - badEntries = new List(); - } + badEntries ??= new List(); bool presentInGoodEntries = IsPs1xmlFileHelper_IsPresentInEntries(cimModuleFile, goodEntries); bool presentInBadEntries = IsPs1xmlFileHelper_IsPresentInEntries(cimModuleFile, badEntries); @@ -1841,7 +1866,7 @@ protected override void ProcessRecord() // of doing Get-Module -list foreach (PSModuleInfo module in ModuleInfo) { - ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, module.Name); + ApplicationInsightsTelemetry.SendModuleTelemetryMetric(TelemetryType.ModuleLoad, module.Name, module.Version?.ToString()); RemoteDiscoveryHelper.DispatchModuleInfoProcessing( module, localAction: () => @@ -1867,13 +1892,11 @@ protected override void ProcessRecord() else if (this.ParameterSetName.Equals(ParameterSet_Assembly, StringComparison.OrdinalIgnoreCase)) { // Now load all of the supplied assemblies... - if (Assembly != null) + foreach (Assembly suppliedAssembly in Assembly) { - foreach (Assembly suppliedAssembly in Assembly) - { - ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, suppliedAssembly.GetName().Name); - ImportModule_ViaAssembly(importModuleOptions, suppliedAssembly); - } + // we don't know what the version of the module is. + ApplicationInsightsTelemetry.SendModuleTelemetryMetric(TelemetryType.ModuleLoad, suppliedAssembly.GetName().Name); + ImportModule_ViaAssembly(importModuleOptions, suppliedAssembly); } } else if (this.ParameterSetName.Equals(ParameterSet_Name, StringComparison.OrdinalIgnoreCase)) @@ -1903,7 +1926,7 @@ protected override void ProcessRecord() ImportModule_RemotelyViaPsrpSession(importModuleOptions, null, FullyQualifiedName, this.PSSession); foreach (ModuleSpecification modulespec in FullyQualifiedName) { - ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ModuleLoad, modulespec.Name); + ApplicationInsightsTelemetry.SendModuleTelemetryMetric(TelemetryType.ModuleLoad, modulespec.Name, modulespec.Version?.ToString()); } } else if (this.ParameterSetName.Equals(ParameterSet_ViaWinCompat, StringComparison.OrdinalIgnoreCase) @@ -1945,27 +1968,26 @@ private bool IsModuleInDenyList(string[] moduleDenyList, string moduleName, Modu return match; } - private List FilterModuleCollection(IEnumerable moduleCollection) + private IEnumerable FilterModuleCollection(IEnumerable moduleCollection) { - List filteredModuleCollection = null; - if (moduleCollection != null) + if (moduleCollection is null) { - // the ModuleDeny list is cached in PowerShellConfig object - string[] moduleDenyList = PowerShellConfig.Instance.GetWindowsPowerShellCompatibilityModuleDenyList(); - if (moduleDenyList?.Any() != true) - { - filteredModuleCollection = new List(moduleCollection); - } - else + return null; + } + + // The ModuleDeny list is cached in PowerShellConfig object + string[] moduleDenyList = PowerShellConfig.Instance.GetWindowsPowerShellCompatibilityModuleDenyList(); + if (moduleDenyList is null || moduleDenyList.Length == 0) + { + return moduleCollection; + } + + var filteredModuleCollection = new List(); + foreach (var module in moduleCollection) + { + if (!IsModuleInDenyList(moduleDenyList, module as string, module as ModuleSpecification)) { - filteredModuleCollection = new List(); - foreach (var module in moduleCollection) - { - if (!IsModuleInDenyList(moduleDenyList, module as string, module as ModuleSpecification)) - { - filteredModuleCollection.Add(module); - } - } + filteredModuleCollection.Add(module); } } @@ -1977,38 +1999,73 @@ private void PrepareNoClobberWinCompatModuleImport(string moduleName, ModuleSpec Debug.Assert(string.IsNullOrEmpty(moduleName) ^ (moduleSpec == null), "Either moduleName or moduleSpec must be specified"); // moduleName can be just a module name and it also can be a full path to psd1 from which we need to extract the module name - string coreModuleToLoad = ModuleIntrinsics.GetModuleName(moduleSpec == null ? moduleName : moduleSpec.Name); + string moduleToLoad = ModuleIntrinsics.GetModuleName(moduleSpec is null ? moduleName : moduleSpec.Name); + + var isBuiltInModule = BuiltInModules.TryGetValue(moduleToLoad, out string normalizedName); + if (isBuiltInModule) + { + moduleToLoad = normalizedName; + } - var isModuleToLoadEngineModule = InitialSessionState.IsEngineModule(coreModuleToLoad); string[] noClobberModuleList = PowerShellConfig.Instance.GetWindowsPowerShellCompatibilityNoClobberModuleList(); - if (isModuleToLoadEngineModule || ((noClobberModuleList != null) && noClobberModuleList.Contains(coreModuleToLoad, StringComparer.OrdinalIgnoreCase))) + if (isBuiltInModule || noClobberModuleList?.Contains(moduleToLoad, StringComparer.OrdinalIgnoreCase) == true) { - // if it is one of engine modules - first try to load it from $PSHOME\Modules - // otherwise rely on $env:PSModulePath (in which WinPS module location has to go after CorePS module location) - if (isModuleToLoadEngineModule) + bool shouldLoadModuleLocally = true; + if (isBuiltInModule) { - string expectedCoreModulePath = Path.Combine(ModuleIntrinsics.GetPSHomeModulePath(), coreModuleToLoad); - if (Directory.Exists(expectedCoreModulePath)) + PSSnapInInfo loadedSnapin = Context.CurrentRunspace.InitialSessionState.GetPSSnapIn(moduleToLoad); + shouldLoadModuleLocally = loadedSnapin is null; + + if (shouldLoadModuleLocally) { - coreModuleToLoad = expectedCoreModulePath; + // If it is one of built-in modules, first try loading it from $PSHOME\Modules, otherwise rely on $env:PSModulePath. + string expectedCoreModulePath = Path.Combine(ModuleIntrinsics.GetPSHomeModulePath(), moduleToLoad); + if (Directory.Exists(expectedCoreModulePath)) + { + moduleToLoad = expectedCoreModulePath; + } } } - if (moduleSpec == null) - { - ImportModule_LocallyViaName_WithTelemetry(importModuleOptions, coreModuleToLoad); - } - else + if (shouldLoadModuleLocally) { - ModuleSpecification tmpModuleSpec = new ModuleSpecification() + // Here we want to load a core-edition compatible version of the module, so the loading procedure will skip + // the 'System32' module path when searching. Also, we want to suppress writing out errors in case that a + // core-compatible version of the module cannot be found, because: + // 1. that's OK as long as it's not a PowerShell built-in module such as the 'Utility' moudle; + // 2. the error message will be confusing to the user. + bool savedValue = importModuleOptions.SkipSystem32ModulesAndSuppressError; + importModuleOptions.SkipSystem32ModulesAndSuppressError = true; + + PSModuleInfo moduleInfo = moduleSpec is null + ? ImportModule_LocallyViaName_WithTelemetry(importModuleOptions, moduleToLoad) + : ImportModule_LocallyViaFQName( + importModuleOptions, + new ModuleSpecification() + { + Guid = moduleSpec.Guid, + MaximumVersion = moduleSpec.MaximumVersion, + Version = moduleSpec.Version, + RequiredVersion = moduleSpec.RequiredVersion, + Name = moduleToLoad + }); + + // If we failed to load a core-compatible version of a built-in module, we should stop trying to load the + // module in 'WinCompat' mode and report an error. This could happen when a user didn't correctly deploy + // the built-in modules, which would result in very confusing errors when the module auto-loading silently + // attempts to load those built-in modules in 'WinCompat' mode from the 'System32' module path. + // + // If the loading failed but it's NOT a built-in module, then it's fine to ignore this failure and continue + // to load the module in 'WinCompat' mode. + if (moduleInfo is null && isBuiltInModule) { - Guid = moduleSpec.Guid, - MaximumVersion = moduleSpec.MaximumVersion, - Version = moduleSpec.Version, - RequiredVersion = moduleSpec.RequiredVersion, - Name = coreModuleToLoad - }; - ImportModule_LocallyViaFQName(importModuleOptions, tmpModuleSpec); + throw new InvalidOperationException( + StringUtil.Format( + Modules.CannotFindCoreCompatibleBuiltInModule, + moduleToLoad)); + } + + importModuleOptions.SkipSystem32ModulesAndSuppressError = savedValue; } importModuleOptions.NoClobberExportPSSession = true; @@ -2020,28 +2077,15 @@ internal override IList ImportModulesUsingWinCompat(IEnumerable moduleProxyList = new List(); #if !UNIX // one of the two parameters can be passed: either ModuleNames (most of the time) or ModuleSpecifications (they are used in different parameter sets) - List filteredModuleNames = FilterModuleCollection(moduleNames); - List filteredModuleFullyQualifiedNames = FilterModuleCollection(moduleFullyQualifiedNames); + IEnumerable filteredModuleNames = FilterModuleCollection(moduleNames); + IEnumerable filteredModuleFullyQualifiedNames = FilterModuleCollection(moduleFullyQualifiedNames); // do not setup WinCompat resources if we have no modules to import - if ((filteredModuleNames?.Any() != true) && (filteredModuleFullyQualifiedNames?.Any() != true)) + if (filteredModuleNames?.Any() != true && filteredModuleFullyQualifiedNames?.Any() != true) { return moduleProxyList; } - var winPSVersionString = Utils.GetWindowsPowerShellVersionFromRegistry(); - if (!winPSVersionString.StartsWith("5.1", StringComparison.OrdinalIgnoreCase)) - { - string errorMessage = string.Format(CultureInfo.InvariantCulture, Modules.WinCompatRequredVersionError, winPSVersionString); - throw new InvalidOperationException(errorMessage); - } - - PSSession WindowsPowerShellCompatRemotingSession = CreateWindowsPowerShellCompatResources(); - if (WindowsPowerShellCompatRemotingSession == null) - { - return new List(); - } - // perform necessary preparations if module has to be imported with NoClobber mode if (filteredModuleNames != null) { @@ -2059,13 +2103,26 @@ internal override IList ImportModulesUsingWinCompat(IEnumerable(); + } + // perform the module import / proxy generation moduleProxyList = ImportModule_RemotelyViaPsrpSession(importModuleOptions, filteredModuleNames, filteredModuleFullyQualifiedNames, WindowsPowerShellCompatRemotingSession, usingWinCompat: true); foreach (PSModuleInfo moduleProxy in moduleProxyList) { moduleProxy.IsWindowsPowerShellCompatModule = true; - System.Threading.Interlocked.Increment(ref s_WindowsPowerShellCompatUsageCounter); + Interlocked.Increment(ref s_WindowsPowerShellCompatUsageCounter); string message = StringUtil.Format(Modules.WinCompatModuleWarning, moduleProxy.Name, WindowsPowerShellCompatRemotingSession.Name); WriteWarning(message); diff --git a/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs b/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs index 90019e3bfed..48dd73a987b 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs @@ -105,6 +105,12 @@ protected internal struct ImportModuleOptions /// Historically -AllowClobber in these scenarios was set as True. /// internal bool NoClobberExportPSSession; + + /// + /// Flag that controls skipping the System32 module path when searching a module in module paths. It also suppresses + /// writing out errors when specified. + /// + internal bool SkipSystem32ModulesAndSuppressError; } /// @@ -280,6 +286,21 @@ internal List MatchAll "ModuleVersion" }; + /// + /// List of PowerShell built-in modules that are shipped with PowerShell only, not on PS Gallery. + /// + protected static readonly HashSet BuiltInModules = new(StringComparer.OrdinalIgnoreCase) + { + "CimCmdlets", + "Microsoft.PowerShell.Diagnostics", + "Microsoft.PowerShell.Host", + "Microsoft.PowerShell.Management", + "Microsoft.PowerShell.Security", + "Microsoft.PowerShell.Utility", + "Microsoft.WSMan.Management", + "PSDiagnostics", + }; + /// /// When module manifests lack a CompatiblePSEditions field, /// they will be treated as if they have this value. @@ -307,14 +328,25 @@ internal List MatchAll private readonly Dictionary _currentlyProcessingModules = new Dictionary(); - internal bool LoadUsingModulePath(bool found, IEnumerable modulePath, string name, SessionState ss, - ImportModuleOptions options, ManifestProcessingFlags manifestProcessingFlags, out PSModuleInfo module) + internal bool LoadUsingModulePath( + IEnumerable modulePath, + string name, + SessionState ss, + ImportModuleOptions options, + ManifestProcessingFlags manifestProcessingFlags, + out PSModuleInfo module) { - return LoadUsingModulePath(null, found, modulePath, name, ss, options, manifestProcessingFlags, out module); + return LoadUsingModulePath(parentModule: null, modulePath, name, ss, options, manifestProcessingFlags, out module); } - internal bool LoadUsingModulePath(PSModuleInfo parentModule, bool found, IEnumerable modulePath, string name, SessionState ss, - ImportModuleOptions options, ManifestProcessingFlags manifestProcessingFlags, out PSModuleInfo module) + internal bool LoadUsingModulePath( + PSModuleInfo parentModule, + IEnumerable modulePath, + string name, + SessionState ss, + ImportModuleOptions options, + ManifestProcessingFlags manifestProcessingFlags, + out PSModuleInfo module) { string extension = Path.GetExtension(name); string fileBaseName; @@ -325,11 +357,18 @@ internal bool LoadUsingModulePath(PSModuleInfo parentModule, bool found, IEnumer extension = null; } else + { fileBaseName = name.Substring(0, name.Length - extension.Length); + } // Now search using the module path... + bool found = false; foreach (string path in modulePath) { + if (options.SkipSystem32ModulesAndSuppressError && ModuleUtils.IsOnSystem32ModulePath(path)) + { + continue; + } #if UNIX foreach (string folder in Directory.EnumerateDirectories(path)) { @@ -643,9 +682,19 @@ private bool ValidateManifestHash( return result; } - private PSModuleInfo LoadModuleNamedInManifest(PSModuleInfo parentModule, ModuleSpecification moduleSpecification, string moduleBase, bool searchModulePath, - string prefix, SessionState ss, ImportModuleOptions options, ManifestProcessingFlags manifestProcessingFlags, bool loadTypes, - bool loadFormats, object privateData, out bool found, string shortModuleName, PSLanguageMode? manifestLanguageMode) + private PSModuleInfo LoadModuleNamedInManifest( + PSModuleInfo parentModule, + ModuleSpecification moduleSpecification, + string moduleBase, + bool searchModulePath, + string prefix, + SessionState ss, + ImportModuleOptions options, + ManifestProcessingFlags manifestProcessingFlags, + object privateData, + out bool found, + string shortModuleName, + PSLanguageMode? manifestLanguageMode) { PSModuleInfo module = null; PSModuleInfo tempModuleInfoFromVerification = null; @@ -659,11 +708,16 @@ private PSModuleInfo LoadModuleNamedInManifest(PSModuleInfo parentModule, Module var importingModule = manifestProcessingFlags.HasFlag(ManifestProcessingFlags.LoadElements); string extension = Path.GetExtension(moduleSpecification.Name); + // First check for fully-qualified paths - either absolute or relative string rootedPath = ResolveRootedFilePath(moduleSpecification.Name, this.Context); if (string.IsNullOrEmpty(rootedPath)) { - rootedPath = FixupFileName(moduleBase, moduleSpecification.Name, extension, importingModule); + // Use the name of the parent module if it's specified, otherwise, use the current module name. + // - If the current module is a nested module, then the parent module will be specifeid. + // - If the current module is a root module, then the parent module will not be specified. + string moduleName = parentModule?.Name ?? ModuleIntrinsics.GetModuleName(moduleSpecification.Name); + rootedPath = FixFileName(moduleName, moduleBase, moduleSpecification.Name, extension: null, canLoadAssembly: importingModule); } else { @@ -823,7 +877,7 @@ private PSModuleInfo LoadModuleNamedInManifest(PSModuleInfo parentModule, Module } // Otherwise try the module path - found = LoadUsingModulePath(parentModule, found, modulePath, + found = LoadUsingModulePath(parentModule, modulePath, moduleSpecification.Name, ss, options, manifestProcessingFlags, out module); } @@ -886,8 +940,6 @@ private PSModuleInfo LoadModuleNamedInManifest(PSModuleInfo parentModule, Module options, manifestProcessingFlags, prefix, - loadTypes, - loadFormats, out found, shortModuleName, disableFormatUpdates: false); @@ -1485,6 +1537,7 @@ internal PSModuleInfo LoadModuleManifest( Dbg.Assert(moduleManifestPath != null, "moduleManifestPath for module (.psd1) can't be null"); string moduleBase = Path.GetDirectoryName(moduleManifestPath); + string moduleName = ModuleIntrinsics.GetModuleName(moduleManifestPath); if ((manifestProcessingFlags & (ManifestProcessingFlags.LoadElements | ManifestProcessingFlags.WriteErrors | @@ -1585,24 +1638,32 @@ internal PSModuleInfo LoadModuleManifest( invalidOperation.SetErrorId("Modules_WildCardNotAllowedInModuleToProcessAndInNestedModules"); throw invalidOperation; } + // See if this module is already loaded. Since the manifest entry may not // have an extension and the module table is indexed by full names, we // may have search through all the extensions. PSModuleInfo loadedModule = null; - string rootedPath = this.FixupFileName(moduleBase, actualRootModule, extension: null, importingModule); - string mtpExtension = Path.GetExtension(rootedPath); - if (!string.IsNullOrEmpty(mtpExtension) && ModuleIntrinsics.IsPowerShellModuleExtension(mtpExtension)) + string rootedPath = null; + + // For a root module, we use its own module name instead of the manifest module name when calling 'FixFileName'. + // This is because when actually loading the root module later, it won't have access to the parent manifest module, + // and we will use its own name to query for already loaded assemblies from 'Context.AssemblyCache'. + string rootModuleName = ModuleIntrinsics.GetModuleName(actualRootModule); + string extension = Path.GetExtension(actualRootModule); + if (!string.IsNullOrEmpty(extension) && ModuleIntrinsics.IsPowerShellModuleExtension(extension)) { + rootedPath = FixFileName(rootModuleName, moduleBase, actualRootModule, extension: null, canLoadAssembly: importingModule); TryGetFromModuleTable(rootedPath, out loadedModule); } else { foreach (string extensionToTry in ModuleIntrinsics.PSModuleExtensions) { - rootedPath = this.FixupFileName(moduleBase, actualRootModule, extensionToTry, importingModule); - TryGetFromModuleTable(rootedPath, out loadedModule); - if (loadedModule != null) + rootedPath = FixFileName(rootModuleName, moduleBase, actualRootModule, extensionToTry, canLoadAssembly: importingModule); + if (TryGetFromModuleTable(rootedPath, out loadedModule)) + { break; + } } } @@ -1851,7 +1912,9 @@ internal PSModuleInfo LoadModuleManifest( else if ((requiredProcessorArchitecture != ProcessorArchitecture.None) && (requiredProcessorArchitecture != ProcessorArchitecture.MSIL)) { + #pragma warning disable SYSLIB0037 ProcessorArchitecture currentArchitecture = typeof(object).Assembly.GetName().ProcessorArchitecture; + #pragma warning restore SYSLIB0037 if (currentArchitecture != requiredProcessorArchitecture) { @@ -2008,7 +2071,6 @@ internal PSModuleInfo LoadModuleManifest( { bool nameMissingOrEmpty = false; var invalidNames = new List(); - string moduleName = ModuleIntrinsics.GetModuleName(moduleManifestPath); expFeatureList = new List(features.Length); foreach (Hashtable feature in features) @@ -2121,61 +2183,72 @@ internal PSModuleInfo LoadModuleManifest( // Indicates the ISS.Bind() should be called... bool doBind = false; - // Set up to load any required assemblies that have been specified... - List tmpAssemblyList; - List assemblyList = new List(); - List fixedUpAssemblyPathList = new List(); - - if ( - !GetListOfStringsFromData(data, moduleManifestPath, "RequiredAssemblies", manifestProcessingFlags, - out tmpAssemblyList)) + if (!GetListOfStringsFromData( + data, + moduleManifestPath, + "RequiredAssemblies", + manifestProcessingFlags, + out List assemblyList)) { containedErrors = true; - if (bailOnFirstError) return null; + if (bailOnFirstError) + { + return null; + } } - else + else if (assemblyList != null && importingModule) { - if (tmpAssemblyList != null && tmpAssemblyList.Count > 0) + foreach (string assembly in assemblyList) { - foreach (string assembly in tmpAssemblyList) + if (WildcardPattern.ContainsWildcardCharacters(assembly)) { - assemblyList.Add(assembly); + PSInvalidOperationException invalidOperation = PSTraceSource.NewInvalidOperationException( + Modules.WildCardNotAllowedInRequiredAssemblies, + moduleManifestPath); + invalidOperation.SetErrorId("Modules_WildCardNotAllowedInRequiredAssemblies"); + throw invalidOperation; } - } - - if ((assemblyList != null) && importingModule) - { - foreach (string assembly in assemblyList) + else { - if (WildcardPattern.ContainsWildcardCharacters(assembly)) + string fileName = null; + string ext = Path.GetExtension(assembly); + + // Note that we don't need to load the required assemblies eagerly because they will be loaded before + // processing type and format data. So, when calling 'FixupFileName', we only attempt to resolve the + // path, and avoid triggering the loading of the assembly. + if (ModuleIntrinsics.ProcessableAssemblyExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase)) { - PSInvalidOperationException invalidOperation = PSTraceSource.NewInvalidOperationException( - Modules.WildCardNotAllowedInRequiredAssemblies, - moduleManifestPath); - invalidOperation.SetErrorId("Modules_WildCardNotAllowedInRequiredAssemblies"); - throw invalidOperation; + fileName = FixFileNameWithoutLoadingAssembly(moduleBase, assembly, extension: null); } else { - string fileName = FixupFileName(moduleBase, assembly, StringLiterals.PowerShellNgenAssemblyExtension, importingModule, out bool pathIsResolved); - if (!pathIsResolved) + bool isPathResolved = false; + foreach (string extToTry in ModuleIntrinsics.ProcessableAssemblyExtensions) { - fileName = FixupFileName(moduleBase, assembly, StringLiterals.PowerShellILAssemblyExtension, importingModule); + fileName = FixFileNameWithoutLoadingAssembly(moduleBase, assembly, extToTry, out isPathResolved); + if (isPathResolved) + { + break; + } } - string loadMessage = StringUtil.Format(Modules.LoadingFile, "Assembly", fileName); - WriteVerbose(loadMessage); - iss.Assemblies.Add(new SessionStateAssemblyEntry(assembly, fileName)); - fixedUpAssemblyPathList.Add(fileName); + if (!isPathResolved) + { + // We didn't resolve the assembly path, so remove the '.exe' extension that was added in the + // last iteration of the above loop. + int index = fileName.LastIndexOf('.'); + fileName = fileName.Substring(0, index); + } + } - fileName = FixupFileName(moduleBase, assembly, StringLiterals.PowerShellILExecutableExtension, importingModule); - loadMessage = StringUtil.Format(Modules.LoadingFile, "Executable", fileName); - WriteVerbose(loadMessage); - iss.Assemblies.Add(new SessionStateAssemblyEntry(assembly, fileName)); - fixedUpAssemblyPathList.Add(fileName); + WriteVerbose(StringUtil.Format(Modules.LoadingFile, "Assembly", fileName)); - doBind = true; - } + // Set a fake PSModuleInfo object to indicate the module it comes from. + var assemblyEntry = new SessionStateAssemblyEntry(assembly, fileName); + assemblyEntry.SetModule(new PSModuleInfo(moduleName, path: null, context: null, sessionState: null)); + + iss.Assemblies.Add(assemblyEntry); + doBind = true; } } } @@ -2212,8 +2285,7 @@ internal PSModuleInfo LoadModuleManifest( continue; } - string resolvedEntryFileName = ResolveRootedFilePath(entry.FileName, Context) ?? - entry.FileName; + string resolvedEntryFileName = ResolveRootedFilePath(entry.FileName, Context) ?? entry.FileName; if (resolvedEntryFileName.Equals(resolvedFileName, StringComparison.OrdinalIgnoreCase)) { isAlreadyLoaded = true; @@ -2336,7 +2408,7 @@ internal PSModuleInfo LoadModuleManifest( key: "FileList", manifestProcessingFlags, moduleBase, - extension: string.Empty, + extension: null, // Don't check file existence - don't want to change current behavior without feature team discussion. verifyFilesExist: false, out List fileList)) @@ -2375,17 +2447,13 @@ internal PSModuleInfo LoadModuleManifest( { if (importingModule) { - IList moduleProxies = ImportModulesUsingWinCompat(new string[] { moduleManifestPath }, null, options); + IList moduleProxies = ImportModulesUsingWinCompat( + moduleNames: new string[] { moduleManifestPath }, + moduleFullyQualifiedNames: null, + importModuleOptions: options); - // we are loading by a single ManifestPath so expect max of 1 - if (moduleProxies.Count > 0) - { - return moduleProxies[0]; - } - else - { - return null; - } + // We are loading by a single ManifestPath so expect max of 1 + return moduleProxies.Count > 0 ? moduleProxies[0] : null; } } else @@ -2480,38 +2548,27 @@ internal PSModuleInfo LoadModuleManifest( // If there is a session state, set up to import/export commands and variables if (ss != null) { - ss.Internal.SetVariable(SpecialVariables.PSScriptRootVarPath, Path.GetDirectoryName(moduleManifestPath), - true, CommandOrigin.Internal); - ss.Internal.SetVariable(SpecialVariables.PSCommandPathVarPath, moduleManifestPath, true, + ss.Internal.SetVariable( + SpecialVariables.PSScriptRootVarPath, + moduleBase, + asValue: true, CommandOrigin.Internal); + + ss.Internal.SetVariable( + SpecialVariables.PSCommandPathVarPath, + moduleManifestPath, + asValue: true, + CommandOrigin.Internal); + ss.Internal.Module = manifestInfo; // without ModuleToProcess a manifest will export everything by default // (otherwise we want to honour exports from ModuleToProcess) - if (exportedFunctions == null) - { - exportedFunctions = MatchAll; - } - - if (exportedCmdlets == null) - { - exportedCmdlets = MatchAll; - } - - if (exportedVariables == null) - { - exportedVariables = MatchAll; - } - - if (exportedAliases == null) - { - exportedAliases = MatchAll; - } - - if (exportedDscResources == null) - { - exportedDscResources = MatchAll; - } + exportedAliases ??= MatchAll; + exportedCmdlets ??= MatchAll; + exportedDscResources ??= MatchAll; + exportedFunctions ??= MatchAll; + exportedVariables ??= MatchAll; } manifestInfo.Description = description; @@ -2937,8 +2994,6 @@ internal PSModuleInfo LoadModuleManifest( ss: null, options: nestedModuleOptions, manifestProcessingFlags: manifestProcessingFlags, - loadTypes: true, - loadFormats: true, privateData: privateData, found: out found, shortModuleName: null, @@ -3040,8 +3095,6 @@ internal PSModuleInfo LoadModuleManifest( ss: ss, options: options, manifestProcessingFlags: manifestProcessingFlags, - loadTypes: (exportedTypeFiles == null || exportedTypeFiles.Count == 0), // If types files already loaded, don't load snapin files - loadFormats: (exportedFormatFiles == null || exportedFormatFiles.Count == 0), // if format files already loaded, don't load snapin files privateData: privateData, found: out found, shortModuleName: null, @@ -3146,16 +3199,10 @@ internal PSModuleInfo LoadModuleManifest( } } - if (newManifestInfo.RootModule == null) - { - newManifestInfo.RootModule = manifestInfo.RootModule; - } + newManifestInfo.RootModule ??= manifestInfo.RootModule; // If may be the case that a script has already set the PrivateData field in the module // info object, in which case we won't overwrite it. - if (newManifestInfo.PrivateData == null) - { - newManifestInfo.PrivateData = manifestInfo.PrivateData; - } + newManifestInfo.PrivateData ??= manifestInfo.PrivateData; // Assign the PowerShellGet related properties from the module manifest foreach (var tag in manifestInfo.Tags) @@ -3280,10 +3327,7 @@ internal PSModuleInfo LoadModuleManifest( } } - if (newManifestInfo.RootModuleForManifest == null) - { - newManifestInfo.RootModuleForManifest = manifestInfo.RootModuleForManifest; - } + newManifestInfo.RootModuleForManifest ??= manifestInfo.RootModuleForManifest; if (newManifestInfo.DeclaredCmdletExports == null || newManifestInfo.DeclaredCmdletExports.Count == 0) { @@ -3706,7 +3750,7 @@ internal static object IsModuleLoaded(ExecutionContext context, ModuleSpecificat // If the RequiredModule is one of the Engine modules, then they could have been loaded as snapins (using InitialSessionState.CreateDefault()) if (result == null && InitialSessionState.IsEngineModule(requiredModule.Name)) { - result = ModuleCmdletBase.GetEngineSnapIn(context, requiredModule.Name); + result = context.CurrentRunspace.InitialSessionState.GetPSSnapIn(requiredModule.Name); if (result != null) { loaded = true; @@ -4366,8 +4410,6 @@ private bool GetListOfFilesFromData( out List list) { list = null; - - bool importingModule = manifestProcessingFlags.HasFlag(ManifestProcessingFlags.LoadElements); if (!GetListOfStringsFromData(data, moduleManifestPath, key, manifestProcessingFlags, out List listOfStrings)) { return false; @@ -4389,7 +4431,7 @@ private bool GetListOfFilesFromData( { try { - string fixedFileName = FixupFileName(moduleBase, s, extension, importingModule, skipLoading: true); + string fixedFileName = FixFileNameWithoutLoadingAssembly(moduleBase, s, extension); var dir = Path.GetDirectoryName(fixedFileName); if (string.Equals(psHome, dir, StringComparison.OrdinalIgnoreCase) || @@ -4544,12 +4586,22 @@ internal bool GetScalarFromData( } } + private string FixFileNameWithoutLoadingAssembly(string moduleBase, string fileName, string extension) + { + return FixFileName(moduleName: null, moduleBase, fileName, extension, canLoadAssembly: false, pathIsResolved: out _); + } + + private string FixFileNameWithoutLoadingAssembly(string moduleBase, string fileName, string extension, out bool pathIsResolved) + { + return FixFileName(moduleName: null, moduleBase, fileName, extension, canLoadAssembly: false, out pathIsResolved); + } + /// /// A utility routine to fix up a file name so it's rooted and has an extension. /// - internal string FixupFileName(string moduleBase, string name, string extension, bool isImportingModule, bool skipLoading = false) + private string FixFileName(string moduleName, string moduleBase, string fileName, string extension, bool canLoadAssembly) { - return FixupFileName(moduleBase, name, extension, isImportingModule, pathIsResolved: out _, skipLoading); + return FixFileName(moduleName, moduleBase, fileName, extension, canLoadAssembly, pathIsResolved: out _); } /// @@ -4559,24 +4611,33 @@ internal string FixupFileName(string moduleBase, string name, string extension, /// When fixing up an assembly file, this method loads the resovled assembly if it's in the process of actually loading a module. /// Read the comments in the method for the detailed information. /// + /// Name of the module that we are processing, used for caching purpose when we need to load an assembly. /// The base path to use if the file is not rooted. - /// The file name to resolve. - /// The extension to use in case the given name has no extension. - /// Indicate if we are loading a module. + /// The file name to resolve. + /// The extension to use for the look up. + /// Indicate if we can load assembly for the resolution. /// Indicate if the returned path is fully resolved. - /// Indicate if the resolved module should be loaded. /// - /// The resolved file path. Or, the combined path of and when the file path cannot be resolved. + /// The resolved file path. Or, the combined path of and when the file path cannot be resolved. /// - internal string FixupFileName(string moduleBase, string name, string extension, bool isImportingModule, out bool pathIsResolved, bool skipLoading = false) + private string FixFileName(string moduleName, string moduleBase, string fileName, string extension, bool canLoadAssembly, out bool pathIsResolved) { pathIsResolved = false; - string originalName = name; - string originalExt = Path.GetExtension(name); - if (string.IsNullOrEmpty(originalExt)) + string originalName = fileName; + string originalExt = Path.GetExtension(fileName); + + if (string.IsNullOrEmpty(extension)) { - name += extension; + // When 'extension' is not explicitly specified, we honor the original extension. + extension = originalExt; + } + else if (!extension.Equals(originalExt, StringComparison.OrdinalIgnoreCase)) + { + // When 'extension' is explicitly specified, append it if the original extension is different. + // Note: the original extension could actually be part of the file name. For example, the name + // is `Microsoft.PowerShell.Command.Utility`, in which case the extension is `.Utility`. + fileName += extension; } // Try to get the resolved fully qualified path to the file. @@ -4588,24 +4649,24 @@ internal string FixupFileName(string moduleBase, string name, string extension, // Check for combinedPath in this case will get us the normalized rooted path 'C:\Windows\System32\WindowsPowerShell\v1.0\WSMan.format.ps1xml'. // The 'Microsoft.WSMan.Management' module in PowerShell was updated to not use the relative path for 'FormatsToProcess' entry, // but it's safer to keep the original behavior to avoid unexpected breaking changes. - string combinedPath = Path.Combine(moduleBase, name); - string resolvedPath = IsRooted(name) - ? ResolveRootedFilePath(name, Context) ?? ResolveRootedFilePath(combinedPath, Context) + string combinedPath = Path.Combine(moduleBase, fileName); + string resolvedPath = IsRooted(fileName) + ? ResolveRootedFilePath(fileName, Context) ?? ResolveRootedFilePath(combinedPath, Context) : ResolveRootedFilePath(combinedPath, Context); // Return the path if successfully resolved. - if (resolvedPath != null) + if (resolvedPath is not null) { - if (isImportingModule && resolvedPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) && !skipLoading) + if (canLoadAssembly && resolvedPath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) { // If we are fixing up an assembly file path and we are actually loading the module, then we load the resolved assembly file here. - // This is because we process type/format ps1xml files before 'RootModule' and 'NestedModules' entries during the module loading. - // A types.ps1xml file could refer to a type defined in the assembly that is specified in the 'RootModule' or 'NestedModule', and - // in that case, processing the types.ps1xml file would fail because it happens before processing the 'RootModule', which loads - // the assembly. We cannot move the processing of types.ps1xml file after processing 'RootModule' either, because the 'RootModule' - // might refer to members defined in the types.ps1xml file. In order to make it work for this paradox, we have to load the resolved - // assembly when we are actually loading the module. However, when it's module analysis, there is no need to load the assembly. - ExecutionContext.LoadAssembly(name: null, filename: resolvedPath, error: out _); + // This is because we process type/format ps1xml files before 'RootModule' during the module loading. A types.ps1xml file could + // refer to a type defined in the assembly that is specified in the 'RootModule', and in that case, processing the types.ps1xml file + // would fail because it happens before processing the 'RootModule', which loads the assembly. + // We cannot move the processing of types.ps1xml file after processing 'RootModule' either, because the 'RootModule' might refer to + // members defined in the types.ps1xml file. In order to make it work for this paradox, we have to load the resolved assembly when + // we are actually loading the module. However, when it's module analysis, there is no need to load the assembly. + Context.AddAssembly(source: moduleName, assemblyName: null, filePath: resolvedPath, error: out _); } pathIsResolved = true; @@ -4618,12 +4679,12 @@ internal string FixupFileName(string moduleBase, string name, string extension, // For dlls, we cannot get the path from the provider. // We need to load the assembly and then get the path. // If the module is already loaded, this is not expensive since the assembly is already loaded in the AppDomain - if (!string.IsNullOrEmpty(extension) && + if (canLoadAssembly && !string.IsNullOrEmpty(extension) && (extension.Equals(StringLiterals.PowerShellILAssemblyExtension, StringComparison.OrdinalIgnoreCase) || - extension.Equals(StringLiterals.PowerShellILExecutableExtension, StringComparison.OrdinalIgnoreCase))) + extension.Equals(StringLiterals.PowerShellILExecutableExtension, StringComparison.OrdinalIgnoreCase))) { - Assembly assembly = ExecutionContext.LoadAssembly(name: originalName, filename: null, error: out _); - if (assembly != null) + Assembly assembly = Context.AddAssembly(source: moduleName, assemblyName: originalName, filePath: null, error: out _); + if (assembly is not null) { pathIsResolved = true; result = assembly.Location; @@ -4883,7 +4944,7 @@ internal static void SyncCurrentLocationHandler(object sender, LocationChangedEv } } - internal static System.EventHandler SyncCurrentLocationDelegate; + internal static EventHandler SyncCurrentLocationDelegate; internal virtual IList ImportModulesUsingWinCompat(IEnumerable moduleNames, IEnumerable moduleFullyQualifiedNames, ImportModuleOptions importModuleOptions) { throw new System.NotImplementedException(); } @@ -4937,8 +4998,6 @@ internal void RemoveModule(PSModuleInfo module) /// Module name specified in the cmdlet. internal void RemoveModule(PSModuleInfo module, string moduleNameInRemoveModuleCmdlet) { - bool isTopLevelModule = false; - // if the module path is empty string, means it is a dynamically generated assembly. // We have set the module path to be module name as key to make it unique, we need update here as well in case the module can be removed. if (module.Path == string.Empty) @@ -4946,7 +5005,7 @@ internal void RemoveModule(PSModuleInfo module, string moduleNameInRemoveModuleC module.Path = module.Name; } - bool shouldModuleBeRemoved = ShouldModuleBeRemoved(module, moduleNameInRemoveModuleCmdlet, out isTopLevelModule); + bool shouldModuleBeRemoved = ShouldModuleBeRemoved(module, moduleNameInRemoveModuleCmdlet, out bool isTopLevelModule); if (shouldModuleBeRemoved) { @@ -4954,17 +5013,14 @@ internal void RemoveModule(PSModuleInfo module, string moduleNameInRemoveModuleC if (Context.Modules.ModuleTable.ContainsKey(module.Path)) { // We should try to run OnRemove as the very first thing - if (module.OnRemove != null) - { - module.OnRemove.InvokeUsingCmdlet( - contextCmdlet: this, - useLocalScope: true, - errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, - dollarUnder: AutomationNull.Value, - input: AutomationNull.Value, - scriptThis: AutomationNull.Value, - args: new object[] { module }); - } + module.OnRemove?.InvokeUsingCmdlet( + contextCmdlet: this, + useLocalScope: true, + errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, + dollarUnder: AutomationNull.Value, + input: AutomationNull.Value, + scriptThis: AutomationNull.Value, + args: new object[] { module }); if (module.ImplementingAssembly != null && !module.ImplementingAssembly.IsDynamic) { @@ -5177,6 +5233,15 @@ internal void RemoveModule(PSModuleInfo module, string moduleNameInRemoveModuleC // And the appdomain level module path cache. PSModuleInfo.RemoveFromAppDomainLevelCache(module.Name); + + // And remove the module assembly entries that may have been added from the assembly cache. + Context.RemoveFromAssemblyCache(source: module.Name); + if (module.ModuleType == ModuleType.Binary && !string.IsNullOrEmpty(module.RootModule)) + { + // We also need to clean up the cache entries that are possibly referenced by the root module in this case. + string rootModuleName = ModuleIntrinsics.GetModuleName(module.RootModule); + Context.RemoveFromAssemblyCache(source: rootModuleName); + } } } } @@ -5309,9 +5374,8 @@ internal PSModuleInfo LoadUsingExtensions(PSModuleInfo parentModule, string moduleName, string fileBaseName, string extension, string moduleBase, string prefix, SessionState ss, ImportModuleOptions options, ManifestProcessingFlags manifestProcessingFlags, out bool found) { - bool throwAwayModuleFileFound = false; return LoadUsingExtensions(parentModule, moduleName, fileBaseName, extension, moduleBase, prefix, ss, - options, manifestProcessingFlags, out found, out throwAwayModuleFileFound); + options, manifestProcessingFlags, out found, out _); } /// @@ -5412,7 +5476,6 @@ internal PSModuleInfo LoadUsingExtensions(PSModuleInfo parentModule, } else if (File.Exists(fileName)) { - moduleFileFound = true; // Win8: 325243 - Added the version check so that we do not unload modules with the same name but different version if (BaseForce && DoesAlreadyLoadedModuleSatisfyConstraints(module)) { @@ -5868,6 +5931,7 @@ internal PSModuleInfo LoadModule(PSModuleInfo parentModule, string fileName, str ext.Equals(StringLiterals.PowerShellILExecutableExtension, StringComparison.OrdinalIgnoreCase)) { module = LoadBinaryModule( + parentModule, ModuleIntrinsics.GetModuleName(fileName), fileName, assemblyToLoad: null, @@ -5876,8 +5940,6 @@ internal PSModuleInfo LoadModule(PSModuleInfo parentModule, string fileName, str options, manifestProcessingFlags, prefix, - loadTypes: true, - loadFormats: true, out found); if (found && module != null) @@ -6349,6 +6411,7 @@ private PSModuleInfo AnalyzeScriptFile(string filename, bool force, ExecutionCon /// /// Load a binary module. A binary module is an assembly that should contain cmdlets. /// + /// The parent module for which this module is a nested module. /// The name of the snapin or assembly to load. /// The path to the assembly to load. /// The assembly to load so no lookup need be done. @@ -6360,12 +6423,11 @@ private PSModuleInfo AnalyzeScriptFile(string filename, bool force, ExecutionCon /// /// The set of options that are used while importing a module. /// The manifest processing flags to use when processing the module. - /// Load the types files mentioned in the snapin registration. - /// Load the formst files mentioned in the snapin registration. /// Command name prefix. /// Sets this to true if an assembly was found. /// THe module info object that was created... internal PSModuleInfo LoadBinaryModule( + PSModuleInfo parentModule, string moduleName, string fileName, Assembly assemblyToLoad, @@ -6374,12 +6436,10 @@ internal PSModuleInfo LoadBinaryModule( ImportModuleOptions options, ManifestProcessingFlags manifestProcessingFlags, string prefix, - bool loadTypes, - bool loadFormats, out bool found) { return LoadBinaryModule( - parentModule: null, + parentModule, moduleName, fileName, assemblyToLoad, @@ -6388,8 +6448,6 @@ internal PSModuleInfo LoadBinaryModule( options, manifestProcessingFlags, prefix, - loadTypes, - loadFormats, out found, shortModuleName: null, disableFormatUpdates: false); @@ -6411,8 +6469,6 @@ internal PSModuleInfo LoadBinaryModule( /// The set of options that are used while importing a module. /// The manifest processing flags to use when processing the module. /// Command name prefix. - /// Load the types files mentioned in the snapin registration. - /// Load the formst files mentioned in the snapin registration. /// Sets this to true if an assembly was found. /// Short name for module. /// @@ -6427,44 +6483,36 @@ internal PSModuleInfo LoadBinaryModule( ImportModuleOptions options, ManifestProcessingFlags manifestProcessingFlags, string prefix, - bool loadTypes, - bool loadFormats, out bool found, string shortModuleName, bool disableFormatUpdates) { - PSModuleInfo module = null; - if (string.IsNullOrEmpty(moduleName) && string.IsNullOrEmpty(fileName) && assemblyToLoad == null) + { throw PSTraceSource.NewArgumentNullException("moduleName,fileName,assemblyToLoad"); + } + + bool isParentEngineModule = parentModule != null && InitialSessionState.IsEngineModule(parentModule.Name); // Load the dll and process any cmdlets it might contain... InitialSessionState iss = InitialSessionState.Create(); List detectedCmdlets = null; List> detectedAliases = null; Assembly assembly = null; - Exception error = null; string modulePath = string.Empty; Version assemblyVersion = new Version(0, 0, 0, 0); - var importingModule = (manifestProcessingFlags & ManifestProcessingFlags.LoadElements) != 0; + bool importingModule = (manifestProcessingFlags & ManifestProcessingFlags.LoadElements) != 0; // See if we're loading a straight assembly... if (assemblyToLoad != null) { // Figure out what to use for a module path... - if (!string.IsNullOrEmpty(fileName)) - { - modulePath = fileName; - } - else - { - modulePath = assemblyToLoad.Location; - } + modulePath = string.IsNullOrEmpty(fileName) ? assemblyToLoad.Location : fileName; // And what to use for a module name... if (string.IsNullOrEmpty(moduleName)) { - moduleName = "dynamic_code_module_" + assemblyToLoad.GetName(); + moduleName = "dynamic_code_module_" + assemblyToLoad.FullName; } if (importingModule) @@ -6472,68 +6520,47 @@ internal PSModuleInfo LoadBinaryModule( // Passing module as a parameter here so that the providers can have the module property populated. // For engine providers, the module should point to top-level module name // For FileSystem, the module is Microsoft.PowerShell.Core and not System.Management.Automation - if (parentModule != null && InitialSessionState.IsEngineModule(parentModule.Name)) - { - iss.ImportCmdletsFromAssembly(assemblyToLoad, parentModule); - } - else - { - iss.ImportCmdletsFromAssembly(assemblyToLoad, null); - } + iss.ImportCmdletsFromAssembly(assemblyToLoad, isParentEngineModule ? parentModule : null); } assemblyVersion = GetAssemblyVersionNumber(assemblyToLoad); assembly = assemblyToLoad; - // If this is an in-memory only assembly, add it directly to the assembly cache if - // it isn't already there. - if (string.IsNullOrEmpty(assembly.Location)) - { - if (!Context.AssemblyCache.ContainsKey(assembly.FullName)) - { - Context.AssemblyCache.Add(assembly.FullName, assembly); - } - } + + // Use the parent module name for caching if there is one. + string source = parentModule?.Name ?? moduleName; + // Add it to the assembly cache if it isn't already there. + Context.AddToAssemblyCache(source, assembly); } else if (importingModule) { - assembly = Context.AddAssembly(moduleName, fileName, out error); + // Use the parent module name for caching if there is one. + string source = parentModule?.Name ?? moduleName; + assembly = Context.AddAssembly(source, moduleName, fileName, out Exception error); if (assembly == null) { if (error != null) + { throw error; + } found = false; return null; } assemblyVersion = GetAssemblyVersionNumber(assembly); - - if (string.IsNullOrEmpty(fileName)) - modulePath = assembly.Location; - else - modulePath = fileName; + modulePath = string.IsNullOrEmpty(fileName) ? assembly.Location : fileName; // Passing module as a parameter here so that the providers can have the module property populated. // For engine providers, the module should point to top-level module name // For FileSystem, the module is Microsoft.PowerShell.Core and not System.Management.Automation - if (parentModule != null && InitialSessionState.IsEngineModule(parentModule.Name)) - { - iss.ImportCmdletsFromAssembly(assembly, parentModule); - } - else - { - iss.ImportCmdletsFromAssembly(assembly, null); - } + iss.ImportCmdletsFromAssembly(assembly, isParentEngineModule ? parentModule : null); } else { string binaryPath = fileName; modulePath = fileName; - if (binaryPath == null) - { - binaryPath = System.IO.Path.Combine(moduleBase, moduleName); - } + binaryPath ??= System.IO.Path.Combine(moduleBase, moduleName); BinaryAnalysisResult analysisResult = GetCmdletsFromBinaryModuleImplementation(binaryPath, manifestProcessingFlags, out assemblyVersion); detectedCmdlets = analysisResult.DetectedCmdlets; @@ -6541,58 +6568,25 @@ internal PSModuleInfo LoadBinaryModule( } found = true; - if (string.IsNullOrEmpty(shortModuleName)) - module = new PSModuleInfo(moduleName, modulePath, Context, ss); - else - module = new PSModuleInfo(shortModuleName, modulePath, Context, ss); + string nameToUse = string.IsNullOrEmpty(shortModuleName) ? moduleName : shortModuleName; + PSModuleInfo module = new PSModuleInfo(nameToUse, modulePath, Context, ss); module.SetModuleType(ModuleType.Binary); module.SetModuleBase(moduleBase); module.SetVersion(assemblyVersion); - module.ImplementingAssembly = assemblyToLoad ?? assembly; + module.ImplementingAssembly = assembly; if (importingModule) { SetModuleLoggingInformation(module); } - // Add the types table entries - List typesFileNames = new List(); - foreach (SessionStateTypeEntry sste in iss.Types) - { - typesFileNames.Add(sste.FileName); - } - - if (typesFileNames.Count > 0) - { - module.SetExportedTypeFiles(new ReadOnlyCollection(typesFileNames)); - } - - // Add the format file entries - List formatsFileNames = new List(); - foreach (SessionStateFormatEntry ssfe in iss.Formats) - { - formatsFileNames.Add(ssfe.FileName); - } - - if (formatsFileNames.Count > 0) - { - module.SetExportedFormatFiles(new ReadOnlyCollection(formatsFileNames)); - } - // Add the module info the providers... foreach (SessionStateProviderEntry sspe in iss.Providers) { // For engine providers, the module should point to top-level module name // For FileSystem, the module is Microsoft.PowerShell.Core and not System.Management.Automation - if (parentModule != null && InitialSessionState.IsEngineModule(parentModule.Name)) - { - sspe.SetModule(parentModule); - } - else - { - sspe.SetModule(module); - } + sspe.SetModule(isParentEngineModule ? parentModule : module); } // Add all of the exported cmdlets to the module object... @@ -6706,16 +6700,7 @@ internal PSModuleInfo LoadBinaryModule( iss.Bind(Context, updateOnly: true, module, options.NoClobber, options.Local, setLocation: false); // Scan all of the types in the assembly to register JobSourceAdapters. - IEnumerable allTypes = Array.Empty(); - if (assembly != null) - { - allTypes = assembly.ExportedTypes; - } - else if (assemblyToLoad != null) - { - allTypes = assemblyToLoad.ExportedTypes; - } - + IEnumerable allTypes = assembly?.ExportedTypes ?? Array.Empty(); foreach (Type type in allTypes) { // If it derives from JobSourceAdapter and it's not already registered, register it... @@ -6905,10 +6890,7 @@ internal static void AddModuleToModuleTables(ExecutionContext context, SessionSt targetSessionState.ModuleTableKeys.Add(moduleTableKey); } - if (targetSessionState.Module != null) - { - targetSessionState.Module.AddNestedModule(module); - } + targetSessionState.Module?.AddNestedModule(module); } /// @@ -7210,6 +7192,9 @@ private static void ImportFunctions(FunctionInfo func, SessionStateInternal targ CommandOrigin.Internal, targetSessionState.ExecutionContext); + // Note that the module 'func' and the function table 'functionInfo' instances are now linked + // together (see 'CopiedCommand' in CommandInfo class), so setting visibility on one also + // sets it on the other. SetCommandVisibility(isImportModulePrivate, functionInfo); functionInfo.Module = sourceModule; @@ -7359,29 +7344,6 @@ private static void ValidateCommandName(ModuleCmdletBase cmdlet, } } - /// - /// Search a PSSnapin with the specified name. - /// - internal static PSSnapInInfo GetEngineSnapIn(ExecutionContext context, string name) - { - HashSet snapinSet = new HashSet(); - List cmdlets = context.SessionState.InvokeCommand.GetCmdlets(); - foreach (CmdletInfo cmdlet in cmdlets) - { - PSSnapInInfo snapin = cmdlet.PSSnapIn; - if (snapin != null && !snapinSet.Contains(snapin)) - snapinSet.Add(snapin); - } - - foreach (PSSnapInInfo snapin in snapinSet) - { - if (string.Equals(snapin.Name, name, StringComparison.OrdinalIgnoreCase)) - return snapin; - } - - return null; - } - /// /// Returns the context cached ModuleTable module for import only if found and has safe language boundaries while /// exporting all functions by default. @@ -7394,7 +7356,7 @@ internal static PSSnapInInfo GetEngineSnapIn(ExecutionContext context, string na /// /// Note that module loading order is important with this check when the system is *locked down with DeviceGuard*. /// If a submodule that does not explicitly export any functions is imported from the command line, its useless - /// because no functions are exported (default fn export is explictly disallowed on locked down systems). + /// because no functions are exported (default fn export is explicitly disallowed on locked down systems). /// But if a parentmodule that imports the submodule is then imported, it will get the useless version of the /// module from the ModuleTable and the parent module will not work. /// $mSub = import-module SubModule # No functions exported, useless @@ -7402,7 +7364,7 @@ internal static PSSnapInInfo GetEngineSnapIn(ExecutionContext context, string na /// $mParent.DoSomething # This will likely be broken because SubModule functions are not accessible /// But this is not a realistic scenario because SubModule is useless with DeviceGuard lock down and must explicitly /// export its functions to become useful, at which point this check is no longer in effect and there is no issue. - /// $mSub = import-module SubModule # Explictly exports functions, useful + /// $mSub = import-module SubModule # Explicitly exports functions, useful /// $mParent = import-module ParentModule # This internally imports SubModule /// $mParent.DoSomething # This works because SubModule functions are exported and accessible. /// diff --git a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs index 1d8fe6bc8b5..6c5eae34968 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs @@ -141,10 +141,7 @@ private PSModuleInfo CreateModuleImplementation(string name, string path, object // script scope for the ss. // Allocate the session state instance for this module. - if (ss == null) - { - ss = new SessionState(_context, true, true); - } + ss ??= new SessionState(_context, true, true); // Now set up the module's session state to be the current session state SessionStateInternal oldSessionState = _context.EngineSessionState; @@ -270,7 +267,7 @@ internal List GetModules(string[] patterns, bool all) internal List GetExactMatchModules(string moduleName, bool all, bool exactMatch) { - if (moduleName == null) { moduleName = string.Empty; } + moduleName ??= string.Empty; return GetModuleCore(new string[] { moduleName }, all, exactMatch); } @@ -287,10 +284,7 @@ private List GetModuleCore(string[] patterns, bool all, bool exact } else { - if (patterns == null) - { - patterns = new string[] { "*" }; - } + patterns ??= new string[] { "*" }; foreach (string pattern in patterns) { @@ -721,7 +715,7 @@ internal static bool MatchesModulePath(string modulePath, string requiredPath) string moduleDirPath = Path.GetDirectoryName(modulePath); // The module itself may be in a versioned directory (case 3) - if (Version.TryParse(Path.GetFileName(moduleDirPath), out Version unused)) + if (Version.TryParse(Path.GetFileName(moduleDirPath), out _)) { moduleDirPath = Path.GetDirectoryName(moduleDirPath); } @@ -885,25 +879,35 @@ internal static ExperimentalFeature[] GetExperimentalFeature(string manifestPath } // The extensions of all of the files that can be processed with Import-Module, put the ni.dll in front of .dll to have higher priority to be loaded. - internal static readonly string[] PSModuleProcessableExtensions = new string[] { - StringLiterals.PowerShellDataFileExtension, - StringLiterals.PowerShellScriptFileExtension, - StringLiterals.PowerShellModuleFileExtension, - StringLiterals.PowerShellCmdletizationFileExtension, - StringLiterals.PowerShellNgenAssemblyExtension, - StringLiterals.PowerShellILAssemblyExtension, - StringLiterals.PowerShellILExecutableExtension, - }; + internal static readonly string[] PSModuleProcessableExtensions = new string[] + { + StringLiterals.PowerShellDataFileExtension, + StringLiterals.PowerShellScriptFileExtension, + StringLiterals.PowerShellModuleFileExtension, + StringLiterals.PowerShellCmdletizationFileExtension, + StringLiterals.PowerShellNgenAssemblyExtension, + StringLiterals.PowerShellILAssemblyExtension, + StringLiterals.PowerShellILExecutableExtension, + }; // A list of the extensions to check for implicit module loading and discovery, put the ni.dll in front of .dll to have higher priority to be loaded. - internal static readonly string[] PSModuleExtensions = new string[] { - StringLiterals.PowerShellDataFileExtension, - StringLiterals.PowerShellModuleFileExtension, - StringLiterals.PowerShellCmdletizationFileExtension, - StringLiterals.PowerShellNgenAssemblyExtension, - StringLiterals.PowerShellILAssemblyExtension, - StringLiterals.PowerShellILExecutableExtension, - }; + internal static readonly string[] PSModuleExtensions = new string[] + { + StringLiterals.PowerShellDataFileExtension, + StringLiterals.PowerShellModuleFileExtension, + StringLiterals.PowerShellCmdletizationFileExtension, + StringLiterals.PowerShellNgenAssemblyExtension, + StringLiterals.PowerShellILAssemblyExtension, + StringLiterals.PowerShellILExecutableExtension, + }; + + // A list of the extensions to check for required assemblies. + internal static readonly string[] ProcessableAssemblyExtensions = new string[] + { + StringLiterals.PowerShellNgenAssemblyExtension, + StringLiterals.PowerShellILAssemblyExtension, + StringLiterals.PowerShellILExecutableExtension + }; /// /// Returns true if the extension is one of the module extensions... @@ -915,7 +919,9 @@ internal static bool IsPowerShellModuleExtension(string extension) foreach (string ext in PSModuleProcessableExtensions) { if (extension.Equals(ext, StringComparison.OrdinalIgnoreCase)) + { return true; + } } return false; @@ -958,7 +964,8 @@ internal static string GetPersonalModulePath() #if UNIX return Platform.SelectProductNameForDirectory(Platform.XDG_Type.USER_MODULES); #else - return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Utils.ModuleDirectory); + string myDocumentsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + return string.IsNullOrEmpty(myDocumentsPath) ? null : Path.Combine(myDocumentsPath, Utils.ModuleDirectory); #endif } @@ -976,20 +983,17 @@ internal static string GetPSHomeModulePath() try { string psHome = Utils.DefaultPowerShellAppBase; - if (!string.IsNullOrEmpty(psHome)) - { - // Win8: 584267 Powershell Modules are listed twice in x86, and cannot be removed - // This happens because ModuleTable uses Path as the key and CBS installer - // expands the path to include "SysWOW64" (for - // HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\PowerShell\3\PowerShellEngine ApplicationBase). - // Because of this, the module that is getting loaded during startup (through LocalRunspace) - // is using "SysWow64" in the key. Later, when Import-Module is called, it loads the - // module using ""System32" in the key. #if !UNIX - psHome = psHome.ToLowerInvariant().Replace("\\syswow64\\", "\\system32\\"); + // Win8: 584267 Powershell Modules are listed twice in x86, and cannot be removed. + // This happens because 'ModuleTable' uses Path as the key and x86 WinPS has "SysWOW64" in its $PSHOME. + // Because of this, the module that is getting loaded during startup (through LocalRunspace) is using + // "SysWow64" in the key. Later, when 'Import-Module' is called, it loads the module using ""System32" + // in the key. + // For the cross-platform PowerShell, a user can choose to install it under "C:\Windows\SysWOW64", and + // thus it may have the same problem as described above. So we keep this line of code. + psHome = psHome.ToLowerInvariant().Replace(@"\syswow64\", @"\system32\"); #endif - Interlocked.CompareExchange(ref s_psHomeModulePath, Path.Combine(psHome, "Modules"), null); - } + Interlocked.CompareExchange(ref s_psHomeModulePath, Path.Combine(psHome, "Modules"), null); } catch (System.Security.SecurityException) { @@ -1180,7 +1184,15 @@ public static string GetModulePath(string currentProcessModulePath, string hklmM currentProcessModulePath = hkcuUserModulePath; // = EVT.User } - currentProcessModulePath += Path.PathSeparator; + if (string.IsNullOrEmpty(currentProcessModulePath)) + { + currentProcessModulePath ??= string.Empty; + } + else + { + currentProcessModulePath += Path.PathSeparator; + } + if (string.IsNullOrEmpty(hklmMachineModulePath)) // EVT.Machine does Not exist { currentProcessModulePath += CombineSystemModulePaths(); // += (SharedModulePath + $PSHome\Modules) @@ -1201,11 +1213,23 @@ public static string GetModulePath(string currentProcessModulePath, string hklmM // personalModulePath // sharedModulePath // systemModulePath - currentProcessModulePath = AddToPath(currentProcessModulePath, personalModulePathToUse, 0); - int insertIndex = PathContainsSubstring(currentProcessModulePath, personalModulePathToUse) + personalModulePathToUse.Length + 1; - currentProcessModulePath = AddToPath(currentProcessModulePath, sharedModulePath, insertIndex); - insertIndex = PathContainsSubstring(currentProcessModulePath, sharedModulePath) + sharedModulePath.Length + 1; - currentProcessModulePath = AddToPath(currentProcessModulePath, systemModulePathToUse, insertIndex); + int insertIndex = 0; + if (!string.IsNullOrEmpty(personalModulePathToUse)) + { + currentProcessModulePath = AddToPath(currentProcessModulePath, personalModulePathToUse, insertIndex); + insertIndex = PathContainsSubstring(currentProcessModulePath, personalModulePathToUse) + personalModulePathToUse.Length + 1; + } + + if (!string.IsNullOrEmpty(sharedModulePath)) + { + currentProcessModulePath = AddToPath(currentProcessModulePath, sharedModulePath, insertIndex); + insertIndex = PathContainsSubstring(currentProcessModulePath, sharedModulePath) + sharedModulePath.Length + 1; + } + + if (!string.IsNullOrEmpty(systemModulePathToUse)) + { + currentProcessModulePath = AddToPath(currentProcessModulePath, systemModulePathToUse, insertIndex); + } } return currentProcessModulePath; diff --git a/src/System.Management.Automation/engine/Modules/ModuleUtils.cs b/src/System.Management.Automation/engine/Modules/ModuleUtils.cs index 26d3174ca3b..8dab2283fc5 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleUtils.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleUtils.cs @@ -121,7 +121,7 @@ internal static bool IsPSEditionCompatible( #if UNIX return true; #else - if (!ModuleUtils.IsOnSystem32ModulePath(moduleManifestPath)) + if (!IsOnSystem32ModulePath(moduleManifestPath)) { return true; } diff --git a/src/System.Management.Automation/engine/Modules/NewModuleManifestCommand.cs b/src/System.Management.Automation/engine/Modules/NewModuleManifestCommand.cs index e7cb2f846b3..1035b36e0e6 100644 --- a/src/System.Management.Automation/engine/Modules/NewModuleManifestCommand.cs +++ b/src/System.Management.Automation/engine/Modules/NewModuleManifestCommand.cs @@ -948,12 +948,9 @@ protected override void EndProcessing() // wildcards for exported commands that weren't specified on the command line. if (_rootModule != null || _nestedModules != null || _requiredModules != null) { - if (_exportedFunctions == null) - _exportedFunctions = new string[] { "*" }; - if (_exportedAliases == null) - _exportedAliases = new string[] { "*" }; - if (_exportedCmdlets == null) - _exportedCmdlets = new string[] { "*" }; + _exportedAliases ??= new string[] { "*" }; + _exportedCmdlets ??= new string[] { "*" }; + _exportedFunctions ??= new string[] { "*" }; } ValidateUriParameterValue(ProjectUri, "ProjectUri"); @@ -1030,8 +1027,7 @@ protected override void EndProcessing() result.Append(streamWriter.NewLine); result.Append(streamWriter.NewLine); - if (_rootModule == null) - _rootModule = string.Empty; + _rootModule ??= string.Empty; BuildModuleManifest(result, nameof(RootModule), Modules.RootModule, !string.IsNullOrEmpty(_rootModule), () => QuoteName(_rootModule), streamWriter); diff --git a/src/System.Management.Automation/engine/Modules/PSModuleInfo.cs b/src/System.Management.Automation/engine/Modules/PSModuleInfo.cs index 1c56e8f920b..8dae7db0deb 100644 --- a/src/System.Management.Automation/engine/Modules/PSModuleInfo.cs +++ b/src/System.Management.Automation/engine/Modules/PSModuleInfo.cs @@ -453,7 +453,7 @@ internal void SetVersion(Version version) public ModuleType ModuleType { get; private set; } = ModuleType.Script; /// - /// This this module as being a compiled module... + /// This module as being a compiled module... /// internal void SetModuleType(ModuleType moduleType) { ModuleType = moduleType; } diff --git a/src/System.Management.Automation/engine/Modules/SwitchProcessCommand.cs b/src/System.Management.Automation/engine/Modules/SwitchProcessCommand.cs new file mode 100644 index 00000000000..d660cee6ada --- /dev/null +++ b/src/System.Management.Automation/engine/Modules/SwitchProcessCommand.cs @@ -0,0 +1,134 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Management.Automation; +using System.Runtime.InteropServices; + +using Dbg = System.Management.Automation.Diagnostics; + +#if UNIX + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Implements a cmdlet that allows use of execv API. + /// + [Cmdlet(VerbsCommon.Switch, "Process", HelpUri = "https://go.microsoft.com/fwlink/?linkid=2181448")] + public sealed class SwitchProcessCommand : PSCmdlet + { + /// + /// Get or set the command and arguments to replace the current pwsh process. + /// + [Parameter(Position = 0, Mandatory = false, ValueFromRemainingArguments = true)] + public string[] WithCommand { get; set; } = Array.Empty(); + + /// + /// Execute the command and arguments + /// + protected override void EndProcessing() + { + if (WithCommand.Length == 0) + { + return; + } + + // execv requires command to be full path so resolve command to first match + var command = this.SessionState.InvokeCommand.GetCommand(WithCommand[0], CommandTypes.Application); + if (command is null) + { + ThrowTerminatingError( + new ErrorRecord( + new CommandNotFoundException( + string.Format( + System.Globalization.CultureInfo.InvariantCulture, + CommandBaseStrings.NativeCommandNotFound, + WithCommand[0] + ) + ), + "CommandNotFound", + ErrorCategory.InvalidArgument, + WithCommand[0] + ) + ); + } + + var execArgs = new string?[WithCommand.Length + 1]; + + // execv convention is the first arg is the program name + execArgs[0] = command.Name; + + for (int i = 1; i < WithCommand.Length; i++) + { + execArgs[i] = WithCommand[i]; + } + + // need null terminator at end + execArgs[execArgs.Length - 1] = null; + + var env = Environment.GetEnvironmentVariables(); + var envBlock = new string?[env.Count + 1]; + int j = 0; + foreach (DictionaryEntry entry in env) + { + envBlock[j++] = entry.Key + "=" + entry.Value; + } + + envBlock[envBlock.Length - 1] = null; + + // setup termios for a child process as .NET modifies termios dynamically for use with ReadKey() + ConfigureTerminalForChildProcess(true); + int exitCode = Exec(command.Source, execArgs, envBlock); + if (exitCode < 0) + { + ConfigureTerminalForChildProcess(false); + ThrowTerminatingError( + new ErrorRecord( + new Exception( + string.Format( + System.Globalization.CultureInfo.InvariantCulture, + CommandBaseStrings.ExecFailed, + Marshal.GetLastPInvokeError(), + string.Join(" ", WithCommand) + ) + ), + "ExecutionFailed", + ErrorCategory.InvalidOperation, + WithCommand + ) + ); + } + } + + /// + /// The `execv` POSIX syscall we use to exec /bin/sh. + /// + /// The path to the executable to exec. + /// + /// The arguments to send through to the executable. + /// Array must have its final element be null. + /// + /// + /// The environment variables to send through to the executable in the form of "key=value". + /// Array must have its final element be null. + /// + /// An exit code if exec failed, but if successful the calling process will be overwritten. + /// + [DllImport("libc", + EntryPoint = "execve", + CallingConvention = CallingConvention.Cdecl, + CharSet = CharSet.Ansi, + SetLastError = true)] + private static extern int Exec(string path, string?[] args, string?[] env); + + // leverage .NET runtime's native library which abstracts the need to handle different OS and architectures for termios api + [DllImport("libSystem.Native", EntryPoint = "SystemNative_ConfigureTerminalForChildProcess")] + private static extern void ConfigureTerminalForChildProcess([MarshalAs(UnmanagedType.Bool)] bool childUsesTerminal); + } +} + +#endif diff --git a/src/System.Management.Automation/engine/MshCmdlet.cs b/src/System.Management.Automation/engine/MshCmdlet.cs index c11e674660c..232140d57c5 100644 --- a/src/System.Management.Automation/engine/MshCmdlet.cs +++ b/src/System.Management.Automation/engine/MshCmdlet.cs @@ -283,8 +283,7 @@ public bool HasErrors /// public string ExpandString(string source) { - if (_cmdlet != null) - _cmdlet.ThrowIfStopping(); + _cmdlet?.ThrowIfStopping(); return _context.Engine.Expand(source); } @@ -818,8 +817,7 @@ private Collection InvokeScript( IList input, params object[] args) { - if (_cmdlet != null) - _cmdlet.ThrowIfStopping(); + _cmdlet?.ThrowIfStopping(); Cmdlet cmdletToUse = null; ScriptBlock.ErrorHandlingBehavior errorHandlingBehavior = ScriptBlock.ErrorHandlingBehavior.WriteToExternalErrorPipe; @@ -910,8 +908,7 @@ private Collection InvokeScript( /// public ScriptBlock NewScriptBlock(string scriptText) { - if (_commandRuntime != null) - _commandRuntime.ThrowIfStopping(); + _commandRuntime?.ThrowIfStopping(); ScriptBlock result = ScriptBlock.Create(_context, scriptText); return result; diff --git a/src/System.Management.Automation/engine/MshCommandRuntime.cs b/src/System.Management.Automation/engine/MshCommandRuntime.cs index 3a440478d1a..64202018904 100644 --- a/src/System.Management.Automation/engine/MshCommandRuntime.cs +++ b/src/System.Management.Automation/engine/MshCommandRuntime.cs @@ -928,7 +928,8 @@ private bool InitShouldLogPipelineExecutionDetail() /// internal string PipelineVariable { get; set; } - private PSVariable _pipelineVarReference = null; + private PSVariable _pipelineVarReference; + private bool _shouldRemovePipelineVariable; internal void SetupOutVariable() { @@ -941,13 +942,10 @@ internal void SetupOutVariable() // Handle the creation of OutVariable in the case of Out-Default specially, // as it needs to handle much of its OutVariable support itself. - if ( - (!string.IsNullOrEmpty(this.OutVariable)) && - (!(this.OutVariable.StartsWith('+'))) && - string.Equals("Out-Default", _thisCommand.CommandInfo.Name, StringComparison.OrdinalIgnoreCase)) + if (!OutVariable.StartsWith('+') && + string.Equals("Out-Default", _commandInfo.Name, StringComparison.OrdinalIgnoreCase)) { - if (_state == null) - _state = new SessionState(Context.EngineSessionState); + _state ??= new SessionState(Context.EngineSessionState); IList oldValue = null; oldValue = PSObject.Base(_state.PSVariable.GetValue(this.OutVariable)) as IList; @@ -972,23 +970,34 @@ internal void SetupPipelineVariable() // This can't use the common SetupVariable implementation, as this needs to persist for an entire // pipeline. - if (string.IsNullOrEmpty(this.PipelineVariable)) + if (string.IsNullOrEmpty(PipelineVariable)) { return; } EnsureVariableParameterAllowed(); - if (_state == null) - _state = new SessionState(Context.EngineSessionState); + _state ??= new SessionState(Context.EngineSessionState); // Create the pipeline variable - _pipelineVarReference = new PSVariable(this.PipelineVariable); - _state.PSVariable.Set(_pipelineVarReference); + _pipelineVarReference = new PSVariable(PipelineVariable); + object varToUse = _state.Internal.SetVariable( + _pipelineVarReference, + force: false, + CommandOrigin.Internal); - // Get the reference again in case we re-used one from the - // same scope. - _pipelineVarReference = _state.PSVariable.Get(this.PipelineVariable); + if (ReferenceEquals(_pipelineVarReference, varToUse)) + { + // The returned variable is the exact same instance, which means we set a new variable. + // In this case, we will try removing the pipeline variable in the end. + _shouldRemovePipelineVariable = true; + } + else + { + // A variable with the same name already exists in the same scope and it was returned. + // In this case, we update the reference and don't remove the variable in the end. + _pipelineVarReference = (PSVariable)varToUse; + } if (_thisCommand is not PSScriptCmdlet) { @@ -996,6 +1005,15 @@ internal void SetupPipelineVariable() } } + internal void RemovePipelineVariable() + { + if (_shouldRemovePipelineVariable) + { + // Remove pipeline variable when a pipeline is being torn down. + _state.PSVariable.Remove(PipelineVariable); + } + } + /// /// Configures the number of objects to buffer before calling the downstream Cmdlet. /// @@ -1063,7 +1081,7 @@ internal int OutBuffer /// , /// /// - /// + /// /// namespace Microsoft.Samples.MSH.Cmdlet /// { /// [Cmdlet(VerbsCommon.Remove,"myobjecttype1")] @@ -1086,7 +1104,7 @@ internal int OutBuffer /// } /// } /// } - /// + /// /// /// /// @@ -1157,7 +1175,7 @@ public bool ShouldProcess(string target) /// , /// /// - /// + /// /// namespace Microsoft.Samples.MSH.Cmdlet /// { /// [Cmdlet(VerbsCommon.Remove,"myobjecttype2")] @@ -1180,7 +1198,7 @@ public bool ShouldProcess(string target) /// } /// } /// } - /// + /// /// /// /// @@ -1260,7 +1278,7 @@ public bool ShouldProcess(string target, string action) /// , /// /// - /// + /// /// namespace Microsoft.Samples.MSH.Cmdlet /// { /// [Cmdlet(VerbsCommon.Remove,"myobjecttype3")] @@ -1286,7 +1304,7 @@ public bool ShouldProcess(string target, string action) /// } /// } /// } - /// + /// /// /// /// @@ -1375,7 +1393,7 @@ public bool ShouldProcess( /// , /// /// - /// + /// /// namespace Microsoft.Samples.MSH.Cmdlet /// { /// [Cmdlet(VerbsCommon.Remove,"myobjecttype3")] @@ -1403,7 +1421,7 @@ public bool ShouldProcess( /// } /// } /// } - /// + /// /// /// /// @@ -1681,7 +1699,7 @@ internal ShouldProcessPossibleOptimization CalculatePossibleShouldProcessOptimiz /// to ShouldProcess for the Cmdlet instance. /// /// - /// + /// /// namespace Microsoft.Samples.MSH.Cmdlet /// { /// [Cmdlet(VerbsCommon.Remove,"myobjecttype4")] @@ -1725,7 +1743,7 @@ internal ShouldProcessPossibleOptimization CalculatePossibleShouldProcessOptimiz /// } /// } /// } - /// + /// /// /// /// @@ -1863,7 +1881,7 @@ public bool ShouldContinue( /// to ShouldProcess for the Cmdlet instance. /// /// - /// + /// /// namespace Microsoft.Samples.MSH.Cmdlet /// { /// [Cmdlet(VerbsCommon.Remove,"myobjecttype4")] @@ -1912,7 +1930,7 @@ public bool ShouldContinue( /// } /// } /// } - /// + /// /// /// /// @@ -2377,10 +2395,7 @@ public Exception ManageException(Exception e) if (e == null) throw PSTraceSource.NewArgumentNullException(nameof(e)); - if (PipelineProcessor != null) - { - PipelineProcessor.RecordFailure(e, _thisCommand); - } + PipelineProcessor?.RecordFailure(e, _thisCommand); // 1021203-2005/05/09-JonN // HaltCommandException will cause the command @@ -2404,7 +2419,6 @@ public Exception ManageException(Exception e) } // Log a command health event - MshLog.LogCommandHealthEvent( Context, e, @@ -2543,8 +2557,7 @@ internal void SetupVariable(VariableStreamKind streamKind, string variableName, EnsureVariableParameterAllowed(); - if (_state == null) - _state = new SessionState(Context.EngineSessionState); + _state ??= new SessionState(Context.EngineSessionState); if (variableName.StartsWith('+')) { @@ -2804,8 +2817,16 @@ private void DoWriteError(object obj) _WriteErrorSkipAllowCheck(errorRecord, preference); } - // NOTICE-2004/06/08-JonN 959638 - // Use this variant to skip the ThrowIfWriteNotPermitted check + /// + /// Write an error, skipping the ThrowIfWriteNotPermitted check. + /// + /// The error record to write. + /// The configured error action preference. + /// + /// True when this method is called to write from a native command's stderr stream. + /// When errors are written through a native stderr stream, they do not interact with the error preference system, + /// but must still present as errors in PowerShell. + /// /// /// The pipeline has already been terminated, or was terminated /// during the execution of this method. @@ -2819,7 +2840,7 @@ private void DoWriteError(object obj) /// but the command failure will ultimately be /// , /// - internal void _WriteErrorSkipAllowCheck(ErrorRecord errorRecord, ActionPreference? actionPreference = null, bool isNativeError = false) + internal void _WriteErrorSkipAllowCheck(ErrorRecord errorRecord, ActionPreference? actionPreference = null, bool isFromNativeStdError = false) { ThrowIfStopping(); @@ -2839,7 +2860,7 @@ internal void _WriteErrorSkipAllowCheck(ErrorRecord errorRecord, ActionPreferenc this.PipelineProcessor.LogExecutionError(_thisCommand.MyInvocation, errorRecord); } - if (!isNativeError) + if (!isFromNativeStdError) { this.PipelineProcessor.ExecutionFailed = true; @@ -2905,7 +2926,7 @@ internal void _WriteErrorSkipAllowCheck(ErrorRecord errorRecord, ActionPreferenc // when tracing), so don't add the member again. // We don't add a note property on messages that comes from stderr stream. - if (!isNativeError) + if (!isFromNativeStdError) { errorWrap.WriteStream = WriteStreamType.Error; } @@ -3242,8 +3263,7 @@ internal SwitchParameter WhatIf { if (!IsWhatIfFlagSet && !_isWhatIfPreferenceCached) { - bool defaultUsed = false; - _whatIfFlag = Context.GetBooleanPreference(SpecialVariables.WhatIfPreferenceVarPath, _whatIfFlag, out defaultUsed); + _whatIfFlag = Context.GetBooleanPreference(SpecialVariables.WhatIfPreferenceVarPath, _whatIfFlag, out _); _isWhatIfPreferenceCached = true; } @@ -3736,8 +3756,10 @@ internal void SetVariableListsInPipe() { Diagnostics.Assert(_thisCommand is PSScriptCmdlet, "this is only done for script cmdlets"); - if (_outVarList != null) + if (_outVarList != null && !OutputPipe.IgnoreOutVariableList) { + // A null pipe is used when executing the 'Clean' block of a PSScriptCmdlet. + // In such a case, we don't capture output to the out variable list. this.OutputPipe.AddVariableList(VariableStreamKind.Output, _outVarList); } @@ -3758,26 +3780,13 @@ internal void SetVariableListsInPipe() if (this.PipelineVariable != null) { - // _state can be null if the current script block is dynamicparam, etc. - if (_state != null) - { - // Create the pipeline variable - _state.PSVariable.Set(_pipelineVarReference); - - // Get the reference again in case we re-used one from the - // same scope. - _pipelineVarReference = _state.PSVariable.Get(this.PipelineVariable); - } - this.OutputPipe.SetPipelineVariable(_pipelineVarReference); } } internal void RemoveVariableListsInPipe() { - // Diagnostics.Assert(thisCommand is PSScriptCmdlet, "this is only done for script cmdlets"); - - if (_outVarList != null) + if (_outVarList != null && !OutputPipe.IgnoreOutVariableList) { this.OutputPipe.RemoveVariableList(VariableStreamKind.Output, _outVarList); } @@ -3800,9 +3809,6 @@ internal void RemoveVariableListsInPipe() if (this.PipelineVariable != null) { this.OutputPipe.RemovePipelineVariable(); - // '_state' could be null when a 'DynamicParam' block runs because the 'DynamicParam' block runs in 'DoPrepare', - // before 'PipelineProcessor.SetupParameterVariables' is called, where '_state' is initialized. - _state?.PSVariable.Remove(this.PipelineVariable); } } } diff --git a/src/System.Management.Automation/engine/MshMemberInfo.cs b/src/System.Management.Automation/engine/MshMemberInfo.cs index 73ee243f6e8..4f0b4364bac 100644 --- a/src/System.Management.Automation/engine/MshMemberInfo.cs +++ b/src/System.Management.Automation/engine/MshMemberInfo.cs @@ -1909,9 +1909,18 @@ internal class PSMethodInvocationConstraints internal PSMethodInvocationConstraints( Type methodTargetType, Type[] parameterTypes) + : this(methodTargetType, parameterTypes, genericTypeParameters: null) { - this.MethodTargetType = methodTargetType; - _parameterTypes = parameterTypes; + } + + internal PSMethodInvocationConstraints( + Type methodTargetType, + Type[] parameterTypes, + object[] genericTypeParameters) + { + MethodTargetType = methodTargetType; + ParameterTypes = parameterTypes; + GenericTypeParameters = genericTypeParameters; } /// @@ -1922,9 +1931,12 @@ internal PSMethodInvocationConstraints( /// /// If then there are no constraints /// - public IEnumerable ParameterTypes => _parameterTypes; + public Type[] ParameterTypes { get; } - private readonly Type[] _parameterTypes; + /// + /// Gets the generic type parameters for the method invocation. + /// + public object[] GenericTypeParameters { get; } internal static bool EqualsForCollection(ICollection xs, ICollection ys) { @@ -1946,8 +1958,6 @@ internal static bool EqualsForCollection(ICollection xs, ICollection ys return xs.SequenceEqual(ys); } - // TODO: IEnumerable genericTypeParameters { get; private set; } - public bool Equals(PSMethodInvocationConstraints other) { if (other is null) @@ -1965,7 +1975,12 @@ public bool Equals(PSMethodInvocationConstraints other) return false; } - if (!EqualsForCollection(_parameterTypes, other._parameterTypes)) + if (!EqualsForCollection(ParameterTypes, other.ParameterTypes)) + { + return false; + } + + if (!EqualsForCollection(GenericTypeParameters, other.GenericTypeParameters)) { return false; } @@ -1994,36 +2009,53 @@ public override bool Equals(object obj) } public override int GetHashCode() - { - // algorithm based on https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode - unchecked - { - int result = 61; - - result = result * 397 + (MethodTargetType != null ? MethodTargetType.GetHashCode() : 0); - result = result * 397 + ParameterTypes.SequenceGetHashCode(); - - return result; - } - } + => HashCode.Combine(MethodTargetType, ParameterTypes, GenericTypeParameters); public override string ToString() { StringBuilder sb = new StringBuilder(); string separator = string.Empty; - if (MethodTargetType != null) + if (MethodTargetType is not null) { sb.Append("this: "); sb.Append(ToStringCodeMethods.Type(MethodTargetType, dropNamespaces: true)); separator = " "; } - if (_parameterTypes != null) + if (GenericTypeParameters is not null) + { + sb.Append(separator); + sb.Append("genericTypeParams: "); + + separator = string.Empty; + foreach (object parameter in GenericTypeParameters) + { + sb.Append(separator); + + switch (parameter) + { + case Type paramType: + sb.Append(ToStringCodeMethods.Type(paramType, dropNamespaces: true)); + break; + case ITypeName paramTypeName: + sb.Append(paramTypeName.ToString()); + break; + default: + throw new ArgumentException("Unexpected value"); + } + + separator = ", "; + } + + separator = " "; + } + + if (ParameterTypes is not null) { sb.Append(separator); sb.Append("args: "); separator = string.Empty; - foreach (var p in _parameterTypes) + foreach (var p in ParameterTypes) { sb.Append(separator); sb.Append(ToStringCodeMethods.Type(p, dropNamespaces: true)); @@ -2244,10 +2276,7 @@ public override object Invoke(params object[] arguments) newArguments[i + 1] = arguments[i]; } - if (_codeReferenceMethodInformation == null) - { - _codeReferenceMethodInformation = DotNetAdapter.GetMethodInformationArray(new[] { CodeReference }); - } + _codeReferenceMethodInformation ??= DotNetAdapter.GetMethodInformationArray(new[] { CodeReference }); Adapter.GetBestMethodAndArguments(CodeReference.Name, _codeReferenceMethodInformation, newArguments, out object[] convertedArguments); @@ -2600,10 +2629,7 @@ internal static PSMethod Create(string name, DotNetAdapter dotNetInstanceAdapter return new PSMethod(name, dotNetInstanceAdapter, baseObject, method, isSpecial, isHidden); } - if (method.PSMethodCtor == null) - { - method.PSMethodCtor = CreatePSMethodConstructor(method.methodInformationStructures); - } + method.PSMethodCtor ??= CreatePSMethodConstructor(method.methodInformationStructures); return method.PSMethodCtor.Invoke(name, dotNetInstanceAdapter, baseObject, method, isSpecial, isHidden); } @@ -2659,7 +2685,7 @@ private static Type GetMethodGroupType(MethodInfo methodInfo) return DelegateHelpers.MakeDelegate(methodTypes); } - catch (TypeLoadException) + catch (Exception) { return typeof(Func); } @@ -5035,7 +5061,7 @@ internal struct Enumerator : IEnumerator private readonly PSMemberInfoInternalCollection _allMembers; /// - /// Constructs this instance to enumerate over members. + /// Initializes a new instance of the class to enumerate over members. /// /// Members we are enumerating. internal Enumerator(PSMemberInfoIntegratingCollection integratingCollection) @@ -5063,8 +5089,8 @@ internal Enumerator(PSMemberInfoIntegratingCollection integratingCollection) /// Moves to the next element in the enumeration. /// /// - /// false if there are no more elements to enumerate - /// true otherwise + /// If there are no more elements to enumerate, returns false. + /// Returns true otherwise. /// public bool MoveNext() { @@ -5093,7 +5119,7 @@ public bool MoveNext() } /// - /// Current PSMemberInfo in the enumeration. + /// Gets the current PSMemberInfo in the enumeration. /// /// For invalid arguments. T IEnumerator.Current diff --git a/src/System.Management.Automation/engine/MshObject.cs b/src/System.Management.Automation/engine/MshObject.cs index 7ea7ef1bd72..aa103ea68f9 100644 --- a/src/System.Management.Automation/engine/MshObject.cs +++ b/src/System.Management.Automation/engine/MshObject.cs @@ -674,13 +674,10 @@ internal PSMemberInfoInternalCollection InstanceMembers { lock (_lockObject) { - if (_instanceMembers == null) - { - _instanceMembers = - s_instanceMembersResurrectionTable.GetValue( - GetKeyForResurrectionTables(this), - _ => new PSMemberInfoInternalCollection()); - } + _instanceMembers ??= + s_instanceMembersResurrectionTable.GetValue( + GetKeyForResurrectionTables(this), + _ => new PSMemberInfoInternalCollection()); } } @@ -721,10 +718,7 @@ private AdapterSet InternalAdapterSet { lock (_lockObject) { - if (_adapterSet == null) - { - _adapterSet = GetMappedAdapter(_immediateBaseObject, GetTypeTable()); - } + _adapterSet ??= GetMappedAdapter(_immediateBaseObject, GetTypeTable()); } } @@ -743,10 +737,7 @@ public PSMemberInfoCollection Members { lock (_lockObject) { - if (_members == null) - { - _members = new PSMemberInfoIntegratingCollection(this, s_memberCollection); - } + _members ??= new PSMemberInfoIntegratingCollection(this, s_memberCollection); } } @@ -765,10 +756,7 @@ public PSMemberInfoCollection Properties { lock (_lockObject) { - if (_properties == null) - { - _properties = new PSMemberInfoIntegratingCollection(this, s_propertyCollection); - } + _properties ??= new PSMemberInfoIntegratingCollection(this, s_propertyCollection); } } @@ -787,10 +775,7 @@ public PSMemberInfoCollection Methods { lock (_lockObject) { - if (_methods == null) - { - _methods = new PSMemberInfoIntegratingCollection(this, s_methodCollection); - } + _methods ??= new PSMemberInfoIntegratingCollection(this, s_methodCollection); } } diff --git a/src/System.Management.Automation/engine/MshObjectTypeDescriptor.cs b/src/System.Management.Automation/engine/MshObjectTypeDescriptor.cs index 6e941b4c5f1..11955713c7e 100644 --- a/src/System.Management.Automation/engine/MshObjectTypeDescriptor.cs +++ b/src/System.Management.Automation/engine/MshObjectTypeDescriptor.cs @@ -410,10 +410,7 @@ private void CheckAndAddProperty(PSPropertyInfo propertyInfo, Attribute[] attrib } } - if (propertyAttributes == null) - { - propertyAttributes = new AttributeCollection(); - } + propertyAttributes ??= new AttributeCollection(); typeDescriptor.WriteLine("Adding property \"{0}\".", propertyInfo.Name); diff --git a/src/System.Management.Automation/engine/MshSecurityException.cs b/src/System.Management.Automation/engine/MshSecurityException.cs index 6ba04e4af7c..490de14be02 100644 --- a/src/System.Management.Automation/engine/MshSecurityException.cs +++ b/src/System.Management.Automation/engine/MshSecurityException.cs @@ -93,14 +93,11 @@ public override ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - "UnauthorizedAccess", - ErrorCategory.SecurityError, - null); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + "UnauthorizedAccess", + ErrorCategory.SecurityError, + null); return _errorRecord; } diff --git a/src/System.Management.Automation/engine/NativeCommand.cs b/src/System.Management.Automation/engine/NativeCommand.cs index 822d4cf82d9..80748605a98 100644 --- a/src/System.Management.Automation/engine/NativeCommand.cs +++ b/src/System.Management.Automation/engine/NativeCommand.cs @@ -26,8 +26,7 @@ internal override void DoStopProcessing() { try { - if (_myCommandProcessor != null) - _myCommandProcessor.StopProcessing(); + _myCommandProcessor?.StopProcessing(); } catch (Exception) { diff --git a/src/System.Management.Automation/engine/NativeCommandParameterBinder.cs b/src/System.Management.Automation/engine/NativeCommandParameterBinder.cs index 511449bc7e5..567a00387d5 100644 --- a/src/System.Management.Automation/engine/NativeCommandParameterBinder.cs +++ b/src/System.Management.Automation/engine/NativeCommandParameterBinder.cs @@ -20,8 +20,6 @@ namespace System.Management.Automation /// internal class NativeCommandParameterBinder : ParameterBinderBase { - private readonly VariablePath s_nativeArgumentPassingVarPath = new VariablePath(SpecialVariables.NativeArgumentPassing); - #region ctor /// @@ -85,7 +83,7 @@ internal void BindParameters(Collection parameters) if (parameter.ParameterNameSpecified) { Diagnostics.Assert(!parameter.ParameterText.Contains(' '), "Parameters cannot have whitespace"); - PossiblyGlobArg(parameter.ParameterText, parameter, StringConstantType.BareWord); + PossiblyGlobArg(parameter.ParameterText, parameter, usedQuotes: false); if (parameter.SpaceAfterParameter) { @@ -110,30 +108,22 @@ internal void BindParameters(Collection parameters) // windbg -k com:port=\\devbox\pipe\debug,pipe,resets=0,reconnect // The parser produced an array of strings but marked the parameter so we // can properly reconstruct the correct command line. - StringConstantType stringConstantType = StringConstantType.BareWord; + bool usedQuotes = false; ArrayLiteralAst arrayLiteralAst = null; switch (parameter?.ArgumentAst) { case StringConstantExpressionAst sce: - stringConstantType = sce.StringConstantType; + usedQuotes = sce.StringConstantType != StringConstantType.BareWord; break; case ExpandableStringExpressionAst ese: - stringConstantType = ese.StringConstantType; + usedQuotes = ese.StringConstantType != StringConstantType.BareWord; break; case ArrayLiteralAst ala: arrayLiteralAst = ala; break; } - // Prior to PSNativePSPathResolution experimental feature, a single quote worked the same as a double quote - // so if the feature is not enabled, we treat any quotes as double quotes. When this feature is no longer - // experimental, this code here needs to be removed. - if (!ExperimentalFeature.IsEnabled("PSNativePSPathResolution") && stringConstantType == StringConstantType.SingleQuoted) - { - stringConstantType = StringConstantType.DoubleQuoted; - } - - AppendOneNativeArgument(Context, parameter, argValue, arrayLiteralAst, sawVerbatimArgumentMarker, stringConstantType); + AppendOneNativeArgument(Context, parameter, argValue, arrayLiteralAst, sawVerbatimArgumentMarker, usedQuotes); } } } @@ -175,7 +165,17 @@ internal void AddToArgumentList(CommandParameterInternal parameter, string argum { if (argument != parameter.ParameterText) { - _argumentList.Add(parameter.ParameterText + argument); + // Only combine the text and argument if there was no space after the parameter, + // otherwise, add the parameter and arguments as separate elements. + if (parameter.SpaceAfterParameter) + { + _argumentList.Add(parameter.ParameterText); + _argumentList.Add(argument); + } + else + { + _argumentList.Add(parameter.ParameterText + argument); + } } } else @@ -193,23 +193,17 @@ internal NativeArgumentPassingStyle ArgumentPassingStyle { get { - if (ExperimentalFeature.IsEnabled("PSNativeCommandArgumentPassing")) + try { - try - { - // This will default to the new behavior if it is set to anything other than Legacy - var preference = LanguagePrimitives.ConvertTo( - Context.GetVariableValue(s_nativeArgumentPassingVarPath, NativeArgumentPassingStyle.Standard)); - return preference; - } - catch - { - // The value is not convertable send back Legacy - return NativeArgumentPassingStyle.Legacy; - } + var preference = LanguagePrimitives.ConvertTo( + Context.GetVariableValue(SpecialVariables.NativeArgumentPassingVarPath, NativeArgumentPassingStyle.Standard)); + return preference; + } + catch + { + // The value is not convertable send back Legacy + return NativeArgumentPassingStyle.Legacy; } - - return NativeArgumentPassingStyle.Legacy; } } @@ -227,8 +221,8 @@ internal NativeArgumentPassingStyle ArgumentPassingStyle /// The object to append. /// If the argument was an array literal, the Ast, otherwise null. /// True if the argument occurs after --%. - /// Bare, SingleQuoted, or DoubleQuoted. - private void AppendOneNativeArgument(ExecutionContext context, CommandParameterInternal parameter, object obj, ArrayLiteralAst argArrayAst, bool sawVerbatimArgumentMarker, StringConstantType stringConstantType) + /// True if the argument was a quoted string (single or double). + private void AppendOneNativeArgument(ExecutionContext context, CommandParameterInternal parameter, object obj, ArrayLiteralAst argArrayAst, bool sawVerbatimArgumentMarker, bool usedQuotes) { IEnumerator list = LanguagePrimitives.GetEnumerator(obj); @@ -293,20 +287,11 @@ private void AppendOneNativeArgument(ExecutionContext context, CommandParameterI if (NeedQuotes(arg)) { _arguments.Append('"'); - - if (stringConstantType == StringConstantType.DoubleQuoted) - { - _arguments.Append(ResolvePath(arg, Context)); - AddToArgumentList(parameter, ResolvePath(arg, Context)); - } - else - { - _arguments.Append(arg); - AddToArgumentList(parameter, arg); - } + AddToArgumentList(parameter, arg); // need to escape all trailing backslashes so the native command receives it correctly // according to http://www.daviddeley.com/autohotkey/parameters/parameters.htm#WINCRULESDOC + _arguments.Append(arg); for (int i = arg.Length - 1; i >= 0 && arg[i] == '\\'; i--) { _arguments.Append('\\'); @@ -316,24 +301,24 @@ private void AppendOneNativeArgument(ExecutionContext context, CommandParameterI } else { - if (argArrayAst != null && ArgumentPassingStyle == NativeArgumentPassingStyle.Standard) + if (argArrayAst != null && ArgumentPassingStyle != NativeArgumentPassingStyle.Legacy) { // We have a literal array, so take the extent, break it on spaces and add them to the argument list. foreach (string element in argArrayAst.Extent.Text.Split(' ', StringSplitOptions.RemoveEmptyEntries)) { - PossiblyGlobArg(element, parameter, stringConstantType); + PossiblyGlobArg(element, parameter, usedQuotes); } break; } else { - PossiblyGlobArg(arg, parameter, stringConstantType); + PossiblyGlobArg(arg, parameter, usedQuotes); } } } } - else if (ArgumentPassingStyle == NativeArgumentPassingStyle.Standard && currentObj != null) + else if (ArgumentPassingStyle != NativeArgumentPassingStyle.Legacy && currentObj != null) { // add empty strings to arglist, but not nulls AddToArgumentList(parameter, arg); @@ -348,101 +333,91 @@ private void AppendOneNativeArgument(ExecutionContext context, CommandParameterI /// /// The argument that possibly needs expansion. /// The parameter associated with the operation. - /// Bare, SingleQuoted, or DoubleQuoted. - private void PossiblyGlobArg(string arg, CommandParameterInternal parameter, StringConstantType stringConstantType) + /// True if the argument was a quoted string (single or double). + private void PossiblyGlobArg(string arg, CommandParameterInternal parameter, bool usedQuotes) { var argExpanded = false; #if UNIX // On UNIX systems, we expand arguments containing wildcard expressions against // the file system just like bash, etc. - - if (stringConstantType == StringConstantType.BareWord) + if (!usedQuotes && WildcardPattern.ContainsWildcardCharacters(arg)) { - if (WildcardPattern.ContainsWildcardCharacters(arg)) + // See if the current working directory is a filesystem provider location + // We won't do the expansion if it isn't since native commands can only access the file system. + var cwdinfo = Context.EngineSessionState.CurrentLocation; + + // If it's a filesystem location then expand the wildcards + if (cwdinfo.Provider.Name.Equals(FileSystemProvider.ProviderName, StringComparison.OrdinalIgnoreCase)) { - // See if the current working directory is a filesystem provider location - // We won't do the expansion if it isn't since native commands can only access the file system. - var cwdinfo = Context.EngineSessionState.CurrentLocation; + // On UNIX, paths starting with ~ or absolute paths are not normalized + bool normalizePath = arg.Length == 0 || !(arg[0] == '~' || arg[0] == '/'); - // If it's a filesystem location then expand the wildcards - if (cwdinfo.Provider.Name.Equals(FileSystemProvider.ProviderName, StringComparison.OrdinalIgnoreCase)) + // See if there are any matching paths otherwise just add the pattern as the argument + Collection paths = null; + try { - // On UNIX, paths starting with ~ or absolute paths are not normalized - bool normalizePath = arg.Length == 0 || !(arg[0] == '~' || arg[0] == '/'); - - // See if there are any matching paths otherwise just add the pattern as the argument - Collection paths = null; - try - { - paths = Context.EngineSessionState.InvokeProvider.ChildItem.Get(arg, false); - } - catch - { - // Fallthrough will append the pattern unchanged. - } + paths = Context.EngineSessionState.InvokeProvider.ChildItem.Get(arg, false); + } + catch + { + // Fallthrough will append the pattern unchanged. + } - // Expand paths, but only from the file system. - if (paths?.Count > 0 && paths.All(static p => p.BaseObject is FileSystemInfo)) + // Expand paths, but only from the file system. + if (paths?.Count > 0 && paths.All(p => p.BaseObject is FileSystemInfo)) + { + var sep = string.Empty; + foreach (var path in paths) { - var sep = string.Empty; - foreach (var path in paths) + _arguments.Append(sep); + sep = " "; + var expandedPath = (path.BaseObject as FileSystemInfo).FullName; + if (normalizePath) { - _arguments.Append(sep); - sep = " "; - var expandedPath = (path.BaseObject as FileSystemInfo).FullName; - if (normalizePath) - { - expandedPath = - Context.SessionState.Path.NormalizeRelativePath(expandedPath, cwdinfo.ProviderPath); - } - // If the path contains spaces, then add quotes around it. - if (NeedQuotes(expandedPath)) - { - _arguments.Append('"'); - _arguments.Append(expandedPath); - _arguments.Append('"'); - AddToArgumentList(parameter, expandedPath); - } - else - { - _arguments.Append(expandedPath); - AddToArgumentList(parameter, expandedPath); - } - - argExpanded = true; + expandedPath = + Context.SessionState.Path.NormalizeRelativePath(expandedPath, cwdinfo.ProviderPath); + } + // If the path contains spaces, then add quotes around it. + if (NeedQuotes(expandedPath)) + { + _arguments.Append('"'); + _arguments.Append(expandedPath); + _arguments.Append('"'); + } + else + { + _arguments.Append(expandedPath); } + + AddToArgumentList(parameter, expandedPath); + argExpanded = true; } } } - else + } + else if (!usedQuotes) + { + // Even if there are no wildcards, we still need to possibly + // expand ~ into the filesystem provider home directory path + ProviderInfo fileSystemProvider = Context.EngineSessionState.GetSingleProvider(FileSystemProvider.ProviderName); + string home = fileSystemProvider.Home; + if (string.Equals(arg, "~")) { - // Even if there are no wildcards, we still need to possibly - // expand ~ into the filesystem provider home directory path - ProviderInfo fileSystemProvider = Context.EngineSessionState.GetSingleProvider(FileSystemProvider.ProviderName); - string home = fileSystemProvider.Home; - if (string.Equals(arg, "~")) - { - _arguments.Append(home); - AddToArgumentList(parameter, home); - argExpanded = true; - } - else if (arg.StartsWith("~/", StringComparison.OrdinalIgnoreCase)) - { - string replacementString = string.Concat(home, arg.AsSpan(1)); - _arguments.Append(replacementString); - AddToArgumentList(parameter, replacementString); - argExpanded = true; - } + _arguments.Append(home); + AddToArgumentList(parameter, home); + argExpanded = true; + } + else if (arg.StartsWith("~/", StringComparison.OrdinalIgnoreCase)) + { + var replacementString = string.Concat(home, arg.AsSpan(1)); + _arguments.Append(replacementString); + AddToArgumentList(parameter, replacementString); + argExpanded = true; } } #endif // UNIX - if (stringConstantType != StringConstantType.SingleQuoted) - { - arg = ResolvePath(arg, Context); - } - if (!argExpanded) { _arguments.Append(arg); @@ -450,71 +425,6 @@ private void PossiblyGlobArg(string arg, CommandParameterInternal parameter, Str } } - /// - /// Check if string is prefixed by psdrive, if so, expand it if filesystem path. - /// - /// The potential PSPath to resolve. - /// The current ExecutionContext. - /// Resolved PSPath if applicable otherwise the original path - internal static string ResolvePath(string path, ExecutionContext context) - { - if (ExperimentalFeature.IsEnabled("PSNativePSPathResolution")) - { -#if !UNIX - // on Windows, we need to expand ~ to point to user's home path - if (string.Equals(path, "~", StringComparison.Ordinal) || path.StartsWith(TildeDirectorySeparator, StringComparison.Ordinal) || path.StartsWith(TildeAltDirectorySeparator, StringComparison.Ordinal)) - { - try - { - ProviderInfo fileSystemProvider = context.EngineSessionState.GetSingleProvider(FileSystemProvider.ProviderName); - return new StringBuilder(fileSystemProvider.Home) - .Append(path.AsSpan(1)) - .Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar) - .ToString(); - } - catch - { - return path; - } - } - - // check if the driveName is an actual disk drive on Windows, if so, no expansion - if (path.Length >= 2 && path[1] == ':') - { - foreach (var drive in DriveInfo.GetDrives()) - { - if (drive.Name.StartsWith(new string(path[0], 1), StringComparison.OrdinalIgnoreCase)) - { - return path; - } - } - } -#endif - - if (path.Contains(':')) - { - LocationGlobber globber = new LocationGlobber(context.SessionState); - try - { - ProviderInfo providerInfo; - - // replace the argument with resolved path if it's a filesystem path - string pspath = globber.GetProviderPath(path, out providerInfo); - if (string.Equals(providerInfo.Name, FileSystemProvider.ProviderName, StringComparison.OrdinalIgnoreCase)) - { - path = pspath; - } - } - catch - { - // if it's not a provider path, do nothing - } - } - } - - return path; - } - /// /// Check to see if the string contains spaces and therefore must be quoted. /// @@ -569,8 +479,6 @@ private static string GetEnumerableArgSeparator(ArrayLiteralAst arrayLiteralAst, /// The native command to bind to. /// private readonly NativeCommand _nativeCommand; - private static readonly string TildeDirectorySeparator = $"~{Path.DirectorySeparatorChar}"; - private static readonly string TildeAltDirectorySeparator = $"~{Path.AltDirectorySeparatorChar}"; #endregion private members } diff --git a/src/System.Management.Automation/engine/NativeCommandProcessor.cs b/src/System.Management.Automation/engine/NativeCommandProcessor.cs index 3407532acdb..9a422eab713 100644 --- a/src/System.Management.Automation/engine/NativeCommandProcessor.cs +++ b/src/System.Management.Automation/engine/NativeCommandProcessor.cs @@ -3,21 +3,22 @@ #pragma warning disable 1634, 1691 +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.IO; -using System.ComponentModel; +using System.Management.Automation.Internal; +using System.Management.Automation.Runspaces; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; using System.Text; -using System.Collections; using System.Threading; -using System.Management.Automation.Internal; using System.Xml; -using System.Runtime.InteropServices; using Dbg = System.Management.Automation.Diagnostics; -using System.Runtime.Serialization; -using System.Globalization; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Concurrent; -using System.Collections.Generic; namespace System.Management.Automation { @@ -130,6 +131,100 @@ internal ProcessOutputObject(object data, MinishellStream stream) } } + #nullable enable + /// + /// This exception is used by the NativeCommandProcessor to indicate an error + /// when a native command retuns a non-zero exit code. + /// + [Serializable] + public sealed class NativeCommandExitException : RuntimeException + { + // NOTE: + // When implementing the native error action preference integration, + // reusing ApplicationFailedException was rejected. + // Instead of reusing a type already used in another scenario + // it was decided instead to use a fresh type to avoid conflating the two scenarios: + // * ApplicationFailedException: PowerShell was not able to complete execution of the application. + // * NativeCommandExitException: the application completed execution but returned a non-zero exit code. + + #region Constructors + + /// + /// Initializes a new instance of the class with information on the native + /// command, a specified error message and a specified error ID. + /// + /// The full path of the native command. + /// The exit code returned by the native command. + /// The process ID of the process before it ended. + /// The error message. + /// The PowerShell runtime error ID. + internal NativeCommandExitException(string path, int exitCode, int processId, string message, string errorId) + : base(message) + { + SetErrorId(errorId); + SetErrorCategory(ErrorCategory.NotSpecified); + Path = path; + ExitCode = exitCode; + ProcessId = processId; + } + + /// + /// Initializes a new instance of the class with serialized data. + /// + /// + /// + private NativeCommandExitException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + if (info is null) + { + throw new PSArgumentNullException(nameof(info)); + } + + Path = info.GetString(nameof(Path)); + ExitCode = info.GetInt32(nameof(ExitCode)); + ProcessId = info.GetInt32(nameof(ProcessId)); + } + + #endregion Constructors + + /// + /// Serializes the exception data. + /// + /// Serialization information. + /// Streaming context. + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info is null) + { + throw new PSArgumentNullException(nameof(info)); + } + + base.GetObjectData(info, context); + + info.AddValue(nameof(Path), Path); + info.AddValue(nameof(ExitCode), ExitCode); + info.AddValue(nameof(ProcessId), ProcessId); + } + + /// + /// Gets the path of the native command. + /// + public string? Path { get; } + + /// + /// Gets the exit code returned by the native command. + /// + public int ExitCode { get; } + + /// + /// Gets the native command's process ID. + /// + public int ProcessId { get; } + + } + #nullable restore + /// /// Provides way to create and execute native commands. /// @@ -152,6 +247,8 @@ internal class NativeCommandProcessor : CommandProcessorBase { "cmd", "cscript", + "find", + "sqlcmd", "wscript", }; @@ -326,7 +423,7 @@ internal override void Prepare(IDictionary psDefaultParameterValues) catch (Exception) { // Do cleanup in case of exception - CleanUp(); + CleanUp(killBackgroundProcess: true); throw; } } @@ -348,7 +445,7 @@ internal override void ProcessRecord() catch (Exception) { // Do cleanup in case of exception - CleanUp(); + CleanUp(killBackgroundProcess: true); throw; } } @@ -450,15 +547,14 @@ private void InitNativeProcess() Exception exceptionToRethrow = null; try { - // If this process is being run standalone, tell the host, which might want - // to save off the window title or other such state as might be tweaked by - // the native process + // Before start the executable, tell the host, which might want to save off the + // window title or other such state as might be tweaked by the native process. + Command.Context.EngineHostInterface.NotifyBeginApplication(); + _hasNotifiedBeginApplication = true; + if (_runStandAlone) { - this.Command.Context.EngineHostInterface.NotifyBeginApplication(); - _hasNotifiedBeginApplication = true; - - // Also, store the Raw UI coordinates so that we can scrape the screen after + // Store the Raw UI coordinates so that we can scrape the screen after // if we are transcribing. if (_isTranscribing && (s_supportScreenScrape == true)) { @@ -762,8 +858,35 @@ internal override void Complete() } this.Command.Context.SetVariable(SpecialVariables.LastExitCodeVarPath, _nativeProcess.ExitCode); - if (_nativeProcess.ExitCode != 0) - this.commandRuntime.PipelineProcessor.ExecutionFailed = true; + if (_nativeProcess.ExitCode == 0) + { + return; + } + + this.commandRuntime.PipelineProcessor.ExecutionFailed = true; + + if (!ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandErrorActionPreferenceFeatureName) + || !Command.Context.GetBooleanPreference(SpecialVariables.PSNativeCommandUseErrorActionPreferenceVarPath, defaultPref: false, out _)) + { + return; + } + + const string errorId = nameof(CommandBaseStrings.ProgramExitedWithNonZeroCode); + + string errorMsg = StringUtil.Format( + CommandBaseStrings.ProgramExitedWithNonZeroCode, + NativeCommandName, + _nativeProcess.ExitCode); + + var exception = new NativeCommandExitException( + Path, + _nativeProcess.ExitCode, + _nativeProcess.Id, + errorMsg, + errorId); + + var errorRecord = new ErrorRecord(exception, errorId, ErrorCategory.NotSpecified, targetObject: Path); + this.commandRuntime._WriteErrorSkipAllowCheck(errorRecord); } } catch (Win32Exception e) @@ -782,7 +905,7 @@ internal override void Complete() finally { // Do some cleanup - CleanUp(); + CleanUp(killBackgroundProcess: false); } // An exception was thrown while attempting to run the program @@ -979,15 +1102,9 @@ private static bool IsWindowsApplication(string fileName) #if UNIX return false; #else - if (!Platform.IsWindowsDesktop) { return false; } - - // SHGetFileInfo() does not understand reparse points and returns 0 ("non exe or error") - // so we are trying to get a real path before. - // It is a workaround for Microsoft Store applications. - string realPath = Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods.WinInternalGetTarget(fileName); - if (realPath is not null) + if (!Platform.IsWindowsDesktop) { - fileName = realPath; + return false; } SHFILEINFO shinfo = new SHFILEINFO(); @@ -1045,33 +1162,34 @@ internal void StopProcessing() /// /// Aggressively clean everything up... /// - private void CleanUp() + /// If set, also terminate background process. + private void CleanUp(bool killBackgroundProcess) { // We need to call 'NotifyEndApplication' as appropriate during cleanup if (_hasNotifiedBeginApplication) { - this.Command.Context.EngineHostInterface.NotifyEndApplication(); + Command.Context.EngineHostInterface.NotifyEndApplication(); } try { - if (_nativeProcess != null) - { - // on Unix, we need to kill the process to ensure it terminates as Dispose() merely - // closes the redirected streams and the processs does not exit on macOS. However, - // on Windows, a winexe like notepad should continue running so we don't want to kill it. + // on Unix, we need to kill the process (if not running in background) to ensure it terminates, + // as Dispose() merely closes the redirected streams and the process does not exit. + // However, on Windows, a winexe like notepad should continue running so we don't want to kill it. #if UNIX + if (killBackgroundProcess || !_isRunningInBackground) + { try { - _nativeProcess.Kill(); + _nativeProcess?.Kill(); } catch { - // Ignore all exception since it is cleanup. + // Ignore all exceptions since it is cleanup. } -#endif - _nativeProcess.Dispose(); } +#endif + _nativeProcess?.Dispose(); } catch (Exception) { @@ -1087,7 +1205,7 @@ private void ProcessOutputRecord(ProcessOutputObject outputValue) ErrorRecord record = outputValue.Data as ErrorRecord; Dbg.Assert(record != null, "ProcessReader should ensure that data is ErrorRecord"); record.SetInvocationInfo(this.Command.MyInvocation); - this.commandRuntime._WriteErrorSkipAllowCheck(record, isNativeError: true); + this.commandRuntime._WriteErrorSkipAllowCheck(record, isFromNativeStdError: true); } else if (outputValue.Stream == MinishellStream.Output) { @@ -1162,7 +1280,7 @@ private bool UseSpecialArgumentPassing(string filePath) => /// A boolean that indicates that, when true, output from the process is redirected to a stream, and otherwise is sent to stdout. /// A boolean that indicates that, when true, error output from the process is redirected to a stream, and otherwise is sent to stderr. /// A boolean that indicates that, when true, input to the process is taken from a stream, and otherwise is taken from stdin. - /// A boolean that indicates, when true, that the command to be executed is not part of a pipeline, and otherwise indicates that is is. + /// A boolean that indicates, when true, that the command to be executed is not part of a pipeline, and otherwise indicates that it is. /// A ProcessStartInfo object which is the base of the native invocation. private ProcessStartInfo GetProcessStartInfo( bool redirectOutput, @@ -1265,7 +1383,12 @@ private ProcessStartInfo GetProcessStartInfo( string rawPath = context.EngineSessionState.GetNamespaceCurrentLocation( context.ProviderNames.FileSystem).ProviderPath; - startInfo.WorkingDirectory = WildcardPattern.Unescape(rawPath); + + // Only set this if the PowerShell's current working directory still exists. + if (Directory.Exists(rawPath)) + { + startInfo.WorkingDirectory = WildcardPattern.Unescape(rawPath); + } return startInfo; } @@ -2010,10 +2133,7 @@ internal void Done() { if (_inputFormat == NativeCommandIOFormat.Xml) { - if (_xmlSerializer != null) - { - _xmlSerializer.Done(); - } + _xmlSerializer?.Done(); } else // Text { diff --git a/src/System.Management.Automation/engine/OrderedHashtable.cs b/src/System.Management.Automation/engine/OrderedHashtable.cs new file mode 100644 index 00000000000..d14d3dc2449 --- /dev/null +++ b/src/System.Management.Automation/engine/OrderedHashtable.cs @@ -0,0 +1,246 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Runtime.Serialization; + +#nullable enable + +namespace System.Management.Automation +{ + /// + /// OrderedHashtable is a hashtable that preserves the order of the keys. + /// + public sealed class OrderedHashtable : Hashtable, IEnumerable + { + private readonly OrderedDictionary _orderedDictionary; + + /// + /// Initializes a new instance of the class. + /// + public OrderedHashtable() + { + _orderedDictionary = new OrderedDictionary(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The capacity. + public OrderedHashtable(int capacity) : base(capacity) + { + _orderedDictionary = new OrderedDictionary(capacity); + } + + /// + /// Initializes a new instance of the class. + /// + /// The dictionary to use for initialization. + public OrderedHashtable(IDictionary dictionary) + { + _orderedDictionary = new OrderedDictionary(dictionary.Count); + foreach (DictionaryEntry entry in dictionary) + { + _orderedDictionary.Add(entry.Key, entry.Value); + } + } + + /// + /// Get the number of items in the hashtable. + /// + public override int Count + { + get + { + return _orderedDictionary.Count; + } + } + + /// + /// Get if the hashtable is a fixed size. + /// + public override bool IsFixedSize + { + get + { + return false; + } + } + + /// + /// Get if the hashtable is read-only. + /// + public override bool IsReadOnly + { + get + { + return false; + } + } + + /// + /// Get if the hashtable is synchronized. + /// + public override bool IsSynchronized + { + get + { + return false; + } + } + + /// + /// Gets the keys in the hashtable. + /// + public override ICollection Keys + { + get + { + return _orderedDictionary.Keys; + } + } + + /// + /// Gets the values in the hashtable. + /// + public override ICollection Values + { + get + { + return _orderedDictionary.Values; + } + } + + /// + /// Gets or sets the value associated with the specified key. + /// + /// The key. + /// The value associated with the key. + public override object? this[object key] + { + get + { + return _orderedDictionary[key]; + } + + set + { + _orderedDictionary[key] = value; + } + } + + /// + /// Adds the specified key and value to the hashtable. + /// + /// The key. + /// The value. + public override void Add(object key, object? value) + { + _orderedDictionary.Add(key, value); + } + + /// + /// Removes all keys and values from the hashtable. + /// + public override void Clear() + { + _orderedDictionary.Clear(); + } + + /// + /// Get a shallow clone of the hashtable. + /// + /// A shallow clone of the hashtable. + public override object Clone() + { + return new OrderedHashtable(_orderedDictionary); + } + + /// + /// Determines whether the hashtable contains a specific key. + /// + /// The key to locate in the hashtable. + /// true if the hashtable contains an element with the specified key; otherwise, false. + public override bool Contains(object key) + { + return _orderedDictionary.Contains(key); + } + + /// + /// Determines whether the hashtable contains a specific key. + /// + /// The key to locate in the hashtable. + /// true if the hashtable contains an element with the specified key; otherwise, false. + public override bool ContainsKey(object key) + { + return _orderedDictionary.Contains(key); + } + + /// + /// Determines whether the hashtable contains a specific value. + /// + /// The value to locate in the hashtable. + /// true if the hashtable contains an element with the specified value; otherwise, false. + public override bool ContainsValue(object? value) + { + foreach (DictionaryEntry entry in _orderedDictionary) + { + if (Equals(entry.Value, value)) + { + return true; + } + } + + return false; + } + + /// + /// Copies the elements of the hashtable to an array of type object, starting at the specified array index. + /// + /// The one-dimensional array that is the destination of the elements copied from the hashtable. The array must have zero-based indexing. + /// The zero-based index in array at which copying begins. + public override void CopyTo(Array array, int arrayIndex) + { + _orderedDictionary.CopyTo(array, arrayIndex); + } + + /// + /// Get the enumerator. + /// + /// The enumerator. + public override IDictionaryEnumerator GetEnumerator() + { + return _orderedDictionary.GetEnumerator(); + } + + /// + /// Get the enumerator. + /// + /// The enumerator. + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Returns the data needed to seralize the Hashtable. + /// + /// The serialization info. + /// The serialization context. + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + _orderedDictionary.GetObjectData(info, context); + } + + /// + /// Removes the specified key from the hashtable. + /// + /// The key to remove. + public override void Remove(object key) + { + _orderedDictionary.Remove(key); + } + } +} diff --git a/src/System.Management.Automation/engine/PSVersionInfo.cs b/src/System.Management.Automation/engine/PSVersionInfo.cs index 39fa635e733..99740a44d13 100644 --- a/src/System.Management.Automation/engine/PSVersionInfo.cs +++ b/src/System.Management.Automation/engine/PSVersionInfo.cs @@ -64,6 +64,7 @@ public static class PSVersionInfo private static readonly SemanticVersion s_psV62Version = new SemanticVersion(6, 2, 0, preReleaseLabel: null, buildLabel: null); private static readonly SemanticVersion s_psV7Version = new SemanticVersion(7, 0, 0, preReleaseLabel: null, buildLabel: null); private static readonly SemanticVersion s_psV71Version = new SemanticVersion(7, 1, 0, preReleaseLabel: null, buildLabel: null); + private static readonly SemanticVersion s_psV72Version = new SemanticVersion(7, 2, 0, preReleaseLabel: null, buildLabel: null); private static readonly SemanticVersion s_psSemVersion; private static readonly Version s_psVersion; @@ -110,7 +111,7 @@ static PSVersionInfo() s_psVersionTable[PSVersionInfo.PSVersionName] = s_psSemVersion; s_psVersionTable[PSVersionInfo.PSEditionName] = PSEditionValue; s_psVersionTable[PSGitCommitIdName] = rawGitCommitId; - s_psVersionTable[PSCompatibleVersionsName] = new Version[] { s_psV1Version, s_psV2Version, s_psV3Version, s_psV4Version, s_psV5Version, s_psV51Version, s_psV6Version, s_psV61Version, s_psV62Version, s_psV7Version, s_psV71Version, s_psVersion }; + s_psVersionTable[PSCompatibleVersionsName] = new Version[] { s_psV1Version, s_psV2Version, s_psV3Version, s_psV4Version, s_psV5Version, s_psV51Version, s_psV6Version, s_psV61Version, s_psV62Version, s_psV7Version, s_psV71Version, s_psV72Version, s_psVersion }; s_psVersionTable[PSVersionInfo.SerializationVersionName] = new Version(InternalSerializer.DefaultVersion); s_psVersionTable[PSVersionInfo.PSRemotingProtocolVersionName] = RemotingConstants.ProtocolVersion; s_psVersionTable[PSVersionInfo.WSManStackVersionName] = GetWSManStackVersion(); @@ -728,7 +729,7 @@ private static bool TryParseVersion(string version, ref VersionResult result) } else { - if (dashIndex == -1) + if (plusIndex == -1) { // Here dashIndex == plusIndex == -1 // No preLabel - preLabel == null; @@ -736,6 +737,13 @@ private static bool TryParseVersion(string version, ref VersionResult result) // Format is 'major.minor.patch' versionSansLabel = version; } + else if (dashIndex == -1) + { + // No PreReleaseLabel: preLabel == null + // Format is 'major.minor.patch+BuildLabel' + buildLabel = version.Substring(plusIndex + 1); + versionSansLabel = version.Substring(0, plusIndex); + } else { // Format is 'major.minor.patch-PreReleaseLabel+BuildLabel' diff --git a/src/System.Management.Automation/engine/ParameterBinderBase.cs b/src/System.Management.Automation/engine/ParameterBinderBase.cs index ee7028c3865..e190c4aaca3 100644 --- a/src/System.Management.Automation/engine/ParameterBinderBase.cs +++ b/src/System.Management.Automation/engine/ParameterBinderBase.cs @@ -559,15 +559,13 @@ internal virtual bool BindParameter( parameterMetadata.ObsoleteAttribute.Message); var mshCommandRuntime = this.Command.commandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - // Write out warning only if we are in the context of MshCommandRuntime. - // This is because - // 1. The overload method WriteWarning(WarningRecord) is only available in MshCommandRuntime; - // 2. We write out warnings for obsolete commands and obsolete cmdlet parameters only when in - // the context of MshCommandRuntime. So we do it here to keep consistency. - mshCommandRuntime.WriteWarning(new WarningRecord(FQIDParameterObsolete, obsoleteWarning)); - } + + // Write out warning only if we are in the context of MshCommandRuntime. + // This is because + // 1. The overload method WriteWarning(WarningRecord) is only available in MshCommandRuntime; + // 2. We write out warnings for obsolete commands and obsolete cmdlet parameters only when in + // the context of MshCommandRuntime. So we do it here to keep consistency. + mshCommandRuntime?.WriteWarning(new WarningRecord(FQIDParameterObsolete, obsoleteWarning)); } // Finally bind the argument to the parameter @@ -999,10 +997,7 @@ private object CoerceTypeAsNeeded( // Construct the collection type information if it wasn't passed in. - if (collectionTypeInfo == null) - { - collectionTypeInfo = new ParameterCollectionTypeInformation(toType); - } + collectionTypeInfo ??= new ParameterCollectionTypeInformation(toType); object originalValue = currentValue; object result = currentValue; diff --git a/src/System.Management.Automation/engine/Pipe.cs b/src/System.Management.Automation/engine/Pipe.cs index 8ee000a4faf..d43a0f96a5d 100644 --- a/src/System.Management.Automation/engine/Pipe.cs +++ b/src/System.Management.Automation/engine/Pipe.cs @@ -109,6 +109,13 @@ public override string ToString() /// internal int OutBufferCount { get; set; } = 0; + /// + /// Gets whether the out variable list should be ignored. + /// This is used for scenarios like the `clean` block, where writing to output stream is intentionally + /// disabled and thus out variables should also be ignored. + /// + internal bool IgnoreOutVariableList { get; set; } + /// /// If true, then all input added to this pipe will simply be discarded... /// @@ -226,34 +233,22 @@ internal void AddVariableList(VariableStreamKind kind, IList list) switch (kind) { case VariableStreamKind.Error: - if (_errorVariableList == null) - { - _errorVariableList = new List(); - } + _errorVariableList ??= new List(); _errorVariableList.Add(list); break; case VariableStreamKind.Warning: - if (_warningVariableList == null) - { - _warningVariableList = new List(); - } + _warningVariableList ??= new List(); _warningVariableList.Add(list); break; case VariableStreamKind.Output: - if (_outVariableList == null) - { - _outVariableList = new List(); - } + _outVariableList ??= new List(); _outVariableList.Add(list); break; case VariableStreamKind.Information: - if (_informationVariableList == null) - { - _informationVariableList = new List(); - } + _informationVariableList ??= new List(); _informationVariableList.Add(list); break; @@ -552,15 +547,28 @@ internal object Retrieve() else if (_enumeratorToProcess != null) { if (_enumeratorToProcessIsEmpty) - return AutomationNull.Value; - - if (!ParserOps.MoveNext(_context, null, _enumeratorToProcess)) { - _enumeratorToProcessIsEmpty = true; return AutomationNull.Value; } - return ParserOps.Current(null, _enumeratorToProcess); + while (true) + { + if (!ParserOps.MoveNext(_context, errorPosition: null, _enumeratorToProcess)) + { + _enumeratorToProcessIsEmpty = true; + return AutomationNull.Value; + } + + object retValue = ParserOps.Current(errorPosition: null, _enumeratorToProcess); + if (retValue == AutomationNull.Value) + { + // 'AutomationNull.Value' from the enumerator won't be sent to the pipeline. + // We try to get the next value in this case. + continue; + } + + return retValue; + } } else if (ExternalReader != null) { @@ -595,11 +603,7 @@ internal object Retrieve() /// /// Removes all the objects from the Pipe. /// - internal void Clear() - { - if (ObjectQueue != null) - ObjectQueue.Clear(); - } + internal void Clear() => ObjectQueue?.Clear(); /// /// Returns the currently queued items in the pipe. Note that this will diff --git a/src/System.Management.Automation/engine/ProxyCommand.cs b/src/System.Management.Automation/engine/ProxyCommand.cs index 5703adec664..6a2e6dce66d 100644 --- a/src/System.Management.Automation/engine/ProxyCommand.cs +++ b/src/System.Management.Automation/engine/ProxyCommand.cs @@ -247,6 +247,30 @@ public static string GetEnd(CommandMetadata commandMetadata) return commandMetadata.GetEndBlock(); } + /// + /// This method constructs a string representing the clean block of the command + /// specified by . The returned string only contains the + /// script, it is not enclosed in "clean { }". + /// + /// + /// An instance of CommandMetadata representing a command. + /// + /// + /// A string representing the end block of the command. + /// + /// + /// If is null. + /// + public static string GetClean(CommandMetadata commandMetadata) + { + if (commandMetadata == null) + { + throw PSTraceSource.NewArgumentNullException(nameof(commandMetadata)); + } + + return commandMetadata.GetCleanBlock(); + } + private static T GetProperty(PSObject obj, string property) where T : class { T result = null; diff --git a/src/System.Management.Automation/engine/ScriptCommandProcessor.cs b/src/System.Management.Automation/engine/ScriptCommandProcessor.cs index 99b685e07d2..3fcb66f3180 100644 --- a/src/System.Management.Automation/engine/ScriptCommandProcessor.cs +++ b/src/System.Management.Automation/engine/ScriptCommandProcessor.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Management.Automation.Internal; using System.Management.Automation.Language; +using System.Management.Automation.Runspaces; using System.Reflection; using Dbg = System.Management.Automation.Diagnostics; @@ -47,7 +48,7 @@ protected ScriptCommandProcessorBase(IScriptCommandInfo commandInfo, ExecutionCo protected bool _dontUseScopeCommandOrigin; /// - /// If true, then an exit exception will be rethrown to instead of caught and processed... + /// If true, then an exit exception will be rethrown instead of caught and processed... /// protected bool _rethrowExitException; @@ -142,9 +143,8 @@ internal override bool IsHelpRequested(out string helpTarget, out HelpCategory h if (parameter.IsDashQuestion()) { Dictionary scriptBlockTokenCache = new Dictionary(); - string unused; HelpInfo helpInfo = _scriptBlock.GetHelpInfo(context: Context, commandInfo: CommandInfo, - dontSearchOnRemoteComputer: false, scriptBlockTokenCache: scriptBlockTokenCache, helpFile: out unused, helpUriFromDotLink: out unused); + dontSearchOnRemoteComputer: false, scriptBlockTokenCache: scriptBlockTokenCache, helpFile: out _, helpUriFromDotLink: out _); if (helpInfo == null) { break; @@ -237,6 +237,7 @@ internal sealed class DlrScriptCommandProcessor : ScriptCommandProcessorBase private MutableTuple _localsTuple; private bool _runOptimizedCode; private bool _argsBound; + private bool _anyClauseExecuted; private FunctionContext _functionContext; internal DlrScriptCommandProcessor(ScriptBlock scriptBlock, ExecutionContext context, bool useNewScope, CommandOrigin origin, SessionStateInternal sessionState, object dollarUnderbar) @@ -327,8 +328,7 @@ internal override void DoBegin() ScriptBlock.LogScriptBlockStart(_scriptBlock, Context.CurrentRunspace.InstanceId); - // Even if there is no begin, we need to set up the execution scope for this - // script... + // Even if there is no begin, we need to set up the execution scope for this script... SetCurrentScopeToExecutionScope(); CommandProcessorBase oldCurrentCommandProcessor = Context.CurrentCommandProcessor; try @@ -410,6 +410,7 @@ internal override void Complete() if (_scriptBlock.HasEndBlock) { var endBlock = _runOptimizedCode ? _scriptBlock.EndBlock : _scriptBlock.UnoptimizedEndBlock; + if (this.CommandRuntime.InputPipe.ExternalReader == null) { if (IsPipelineInputExpected()) @@ -433,7 +434,33 @@ internal override void Complete() } finally { - ScriptBlock.LogScriptBlockEnd(_scriptBlock, Context.CurrentRunspace.InstanceId); + if (!_scriptBlock.HasCleanBlock) + { + ScriptBlock.LogScriptBlockEnd(_scriptBlock, Context.CurrentRunspace.InstanceId); + } + } + } + + protected override void CleanResource() + { + if (_scriptBlock.HasCleanBlock && _anyClauseExecuted) + { + // The 'Clean' block doesn't write to pipeline. + Pipe oldOutputPipe = _functionContext._outputPipe; + _functionContext._outputPipe = new Pipe { NullPipe = true }; + + try + { + RunClause( + clause: _runOptimizedCode ? _scriptBlock.CleanBlock : _scriptBlock.UnoptimizedCleanBlock, + dollarUnderbar: AutomationNull.Value, + inputToProcess: AutomationNull.Value); + } + finally + { + _functionContext._outputPipe = oldOutputPipe; + ScriptBlock.LogScriptBlockEnd(_scriptBlock, Context.CurrentRunspace.InstanceId); + } } } @@ -459,6 +486,7 @@ private void RunClause(Action clause, object dollarUnderbar, ob { ExecutionContext.CheckStackDepth(); + _anyClauseExecuted = true; Pipe oldErrorOutputPipe = this.Context.ShellFunctionErrorOutputPipe; // If the script block has a different language mode than the current, @@ -553,7 +581,7 @@ private void RunClause(Action clause, object dollarUnderbar, ob } finally { - this.Context.RestoreErrorPipe(oldErrorOutputPipe); + Context.ShellFunctionErrorOutputPipe = oldErrorOutputPipe; if (oldLanguageMode.HasValue) { @@ -584,15 +612,12 @@ private void RunClause(Action clause, object dollarUnderbar, ob } catch (RuntimeException e) { - ManageScriptException(e); // always throws - // This quiets the compiler which wants to see a return value - // in all codepaths. - throw; + // This method always throws. + ManageScriptException(e); } catch (Exception e) { - // This cmdlet threw an exception, so - // wrap it and bubble it up. + // This cmdlet threw an exception, so wrap it and bubble it up. throw ManageInvocationException(e); } } diff --git a/src/System.Management.Automation/engine/SessionStateContainer.cs b/src/System.Management.Automation/engine/SessionStateContainer.cs index b2d2e3c6837..8a070caec87 100644 --- a/src/System.Management.Automation/engine/SessionStateContainer.cs +++ b/src/System.Management.Automation/engine/SessionStateContainer.cs @@ -1324,8 +1324,8 @@ internal void GetChildItems( try { // If we're recursing, do some path fixups to match user - // expectations: - if (recurse) + // expectations, but only if the last part is a file and not a directory: + if (recurse && !path.EndsWith(Path.DirectorySeparatorChar) && !path.EndsWith(Path.AltDirectorySeparatorChar)) { string childName = GetChildName(path, context); @@ -1434,8 +1434,7 @@ internal void GetChildItems( return; } - int unUsedChildrenNotMatchingFilterCriteria = 0; - ProcessPathItems(providerInstance, providerPath, recurse, depth, context, out unUsedChildrenNotMatchingFilterCriteria, ProcessMode.Enumerate); + ProcessPathItems(providerInstance, providerPath, recurse, depth, context, out _, ProcessMode.Enumerate); } } else @@ -1496,12 +1495,11 @@ internal void GetChildItems( { // Do the recursion manually so that we can apply the // include and exclude filters - int unUsedChildrenNotMatchingFilterCriteria = 0; try { // Temeporary set literal path as false to apply filter context.SuppressWildcardExpansion = false; - ProcessPathItems(providerInstance, path, recurse, depth, context, out unUsedChildrenNotMatchingFilterCriteria, ProcessMode.Enumerate); + ProcessPathItems(providerInstance, path, recurse, depth, context, out _, ProcessMode.Enumerate); } finally { @@ -4089,10 +4087,7 @@ internal Collection CopyItem(string[] paths, throw PSTraceSource.NewArgumentNullException(nameof(paths)); } - if (copyPath == null) - { - copyPath = string.Empty; - } + copyPath ??= string.Empty; CmdletProviderContext context = new CmdletProviderContext(this.ExecutionContext); context.Force = force; @@ -4155,14 +4150,10 @@ internal void CopyItem( throw PSTraceSource.NewArgumentNullException(nameof(paths)); } - if (copyPath == null) - { - copyPath = string.Empty; - } + copyPath ??= string.Empty; // Get the provider specific path for the destination - PSDriveInfo unusedDrive = null; ProviderInfo destinationProvider = null; Microsoft.PowerShell.Commands.CopyItemDynamicParameters dynamicParams = context.DynamicParameters as Microsoft.PowerShell.Commands.CopyItemDynamicParameters; bool destinationIsRemote = false; @@ -4213,7 +4204,7 @@ internal void CopyItem( copyPath, context, out destinationProvider, - out unusedDrive); + out _); } else { diff --git a/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs b/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs index 6869f5071fb..56de07b8871 100644 --- a/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs +++ b/src/System.Management.Automation/engine/SessionStateLocationAPIs.cs @@ -316,10 +316,7 @@ internal PathInfo SetLocation(string path, CmdletProviderContext context, bool l } } - if (context == null) - { - context = new CmdletProviderContext(this.ExecutionContext); - } + context ??= new CmdletProviderContext(this.ExecutionContext); if (CurrentDrive != null) { diff --git a/src/System.Management.Automation/engine/SessionStateNavigation.cs b/src/System.Management.Automation/engine/SessionStateNavigation.cs index f6d1a28eb1c..a1139ebc198 100644 --- a/src/System.Management.Automation/engine/SessionStateNavigation.cs +++ b/src/System.Management.Automation/engine/SessionStateNavigation.cs @@ -1458,7 +1458,6 @@ internal void MoveItem( } else { - PSDriveInfo unusedPSDriveInfo = null; ProviderInfo destinationProvider = null; CmdletProviderContext destinationContext = new CmdletProviderContext(this.ExecutionContext); @@ -1472,7 +1471,7 @@ internal void MoveItem( providerDestinationPaths[0].Path, destinationContext, out destinationProvider, - out unusedPSDriveInfo); + out _); } else { @@ -1484,7 +1483,7 @@ internal void MoveItem( destination, destinationContext, out destinationProvider, - out unusedPSDriveInfo); + out _); } // Now verify the providers are the same. diff --git a/src/System.Management.Automation/engine/SessionStateProviderAPIs.cs b/src/System.Management.Automation/engine/SessionStateProviderAPIs.cs index a6a7d2efc7f..0c94c9ea059 100644 --- a/src/System.Management.Automation/engine/SessionStateProviderAPIs.cs +++ b/src/System.Management.Automation/engine/SessionStateProviderAPIs.cs @@ -960,10 +960,7 @@ internal void InitializeProvider( throw PSTraceSource.NewArgumentNullException(nameof(provider)); } - if (context == null) - { - context = new CmdletProviderContext(this.ExecutionContext); - } + context ??= new CmdletProviderContext(this.ExecutionContext); // Initialize the provider so that it can add any drives // that it needs. diff --git a/src/System.Management.Automation/engine/SessionStateScope.cs b/src/System.Management.Automation/engine/SessionStateScope.cs index fc5dff00784..080aebb2b0a 100644 --- a/src/System.Management.Automation/engine/SessionStateScope.cs +++ b/src/System.Management.Automation/engine/SessionStateScope.cs @@ -1245,11 +1245,18 @@ internal FunctionInfo SetFunction( name != null, "The caller should verify the name"); - var functionInfos = GetFunctions(); - FunctionInfo existingValue; + Dictionary functionInfos = GetFunctions(); FunctionInfo result; - if (!functionInfos.TryGetValue(name, out existingValue)) + + // Functions are equal only if they have the same name and if they come from the same module (if any). + // If the function is not associated with a module then the info 'ModuleName' property is set to empty string. + // If the new function has the same name of an existing function, but different module names, then the + // existing table function is replaced with the new function. + if (!functionInfos.TryGetValue(name, out FunctionInfo existingValue) || + (originalFunction != null && + !existingValue.ModuleName.Equals(originalFunction.ModuleName, StringComparison.OrdinalIgnoreCase))) { + // Add new function info to function table and return. result = functionFactory(name, function, originalFunction, options, context, helpFile); functionInfos[name] = result; @@ -1257,81 +1264,78 @@ internal FunctionInfo SetFunction( { GetAllScopeFunctions()[name] = result; } - } - else - { - // Make sure the function isn't constant or readonly - - SessionState.ThrowIfNotVisible(origin, existingValue); - if (IsFunctionOptionSet(existingValue, ScopedItemOptions.Constant) || - (!force && IsFunctionOptionSet(existingValue, ScopedItemOptions.ReadOnly))) - { - SessionStateUnauthorizedAccessException e = - new SessionStateUnauthorizedAccessException( - name, - SessionStateCategory.Function, - "FunctionNotWritable", - SessionStateStrings.FunctionNotWritable); + return result; + } - throw e; - } + // Update the existing function. - // Ensure we are not trying to set the function to constant as this can only be - // done at creation time. + // Make sure the function isn't constant or readonly. + SessionState.ThrowIfNotVisible(origin, existingValue); - if ((options & ScopedItemOptions.Constant) != 0) - { - SessionStateUnauthorizedAccessException e = - new SessionStateUnauthorizedAccessException( - name, - SessionStateCategory.Function, - "FunctionCannotBeMadeConstant", - SessionStateStrings.FunctionCannotBeMadeConstant); + if (IsFunctionOptionSet(existingValue, ScopedItemOptions.Constant) || + (!force && IsFunctionOptionSet(existingValue, ScopedItemOptions.ReadOnly))) + { + SessionStateUnauthorizedAccessException e = + new SessionStateUnauthorizedAccessException( + name, + SessionStateCategory.Function, + "FunctionNotWritable", + SessionStateStrings.FunctionNotWritable); - throw e; - } + throw e; + } - // Ensure we are not trying to remove the AllScope option + // Ensure we are not trying to set the function to constant as this can only be + // done at creation time. + if ((options & ScopedItemOptions.Constant) != 0) + { + SessionStateUnauthorizedAccessException e = + new SessionStateUnauthorizedAccessException( + name, + SessionStateCategory.Function, + "FunctionCannotBeMadeConstant", + SessionStateStrings.FunctionCannotBeMadeConstant); - if ((options & ScopedItemOptions.AllScope) == 0 && - IsFunctionOptionSet(existingValue, ScopedItemOptions.AllScope)) - { - SessionStateUnauthorizedAccessException e = - new SessionStateUnauthorizedAccessException( - name, - SessionStateCategory.Function, - "FunctionAllScopeOptionCannotBeRemoved", - SessionStateStrings.FunctionAllScopeOptionCannotBeRemoved); + throw e; + } - throw e; - } + // Ensure we are not trying to remove the AllScope option. + if ((options & ScopedItemOptions.AllScope) == 0 && + IsFunctionOptionSet(existingValue, ScopedItemOptions.AllScope)) + { + SessionStateUnauthorizedAccessException e = + new SessionStateUnauthorizedAccessException( + name, + SessionStateCategory.Function, + "FunctionAllScopeOptionCannotBeRemoved", + SessionStateStrings.FunctionAllScopeOptionCannotBeRemoved); - FunctionInfo existingFunction = existingValue; - FunctionInfo newValue = null; + throw e; + } - // If the function type changes (i.e.: function to workflow or back) - // then we need to blast what was there - newValue = functionFactory(name, function, originalFunction, options, context, helpFile); + FunctionInfo existingFunction = existingValue; - bool changesFunctionType = existingFunction.GetType() != newValue.GetType(); + // If the function type changes (i.e.: function to workflow or back) + // then we need to replace what was there. + FunctionInfo newValue = functionFactory(name, function, originalFunction, options, context, helpFile); - // Since the options are set after the script block, we have to - // forcefully apply the script block if the options will be - // set to not being ReadOnly - if (changesFunctionType || - ((existingFunction.Options & ScopedItemOptions.ReadOnly) != 0 && force)) - { - result = newValue; - functionInfos[name] = newValue; - } - else - { - bool applyForce = force || (options & ScopedItemOptions.ReadOnly) == 0; + bool changesFunctionType = existingFunction.GetType() != newValue.GetType(); - existingFunction.Update(newValue, applyForce, options, helpFile); - result = existingFunction; - } + // Since the options are set after the script block, we have to + // forcefully apply the script block if the options will be + // set to not being ReadOnly. + if (changesFunctionType || + ((existingFunction.Options & ScopedItemOptions.ReadOnly) != 0 && force)) + { + result = newValue; + functionInfos[name] = newValue; + } + else + { + bool applyForce = force || (options & ScopedItemOptions.ReadOnly) == 0; + existingFunction.Update(newValue, applyForce, options, helpFile); + result = existingFunction; } return result; @@ -1628,10 +1632,7 @@ internal Language.TypeResolutionState TypeResolutionState internal void AddType(string name, Type type) { - if (TypeTable == null) - { - TypeTable = new Dictionary(StringComparer.OrdinalIgnoreCase); - } + TypeTable ??= new Dictionary(StringComparer.OrdinalIgnoreCase); TypeTable[name] = type; } diff --git a/src/System.Management.Automation/engine/SessionStateVariableAPIs.cs b/src/System.Management.Automation/engine/SessionStateVariableAPIs.cs index 8cc9e4bafa4..336c2b77952 100644 --- a/src/System.Management.Automation/engine/SessionStateVariableAPIs.cs +++ b/src/System.Management.Automation/engine/SessionStateVariableAPIs.cs @@ -352,8 +352,7 @@ internal object GetVariableValueFromProvider( // First get the provider for the path. ProviderInfo providerInfo = null; - string unused = - this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); + _ = this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); throw NewProviderInvocationException( "ProviderCannotBeUsedAsVariable", @@ -368,8 +367,7 @@ internal object GetVariableValueFromProvider( // First get the provider for the path. ProviderInfo providerInfo = null; - string unused = - this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); + _ = this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); throw NewProviderInvocationException( "ProviderCannotBeUsedAsVariable", @@ -407,8 +405,7 @@ internal object GetVariableValueFromProvider( // First get the provider for the path. ProviderInfo providerInfo = null; - string unused = - this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); + _ = this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); throw NewProviderInvocationException( "ProviderVariableSyntaxInvalid", @@ -445,8 +442,7 @@ internal object GetVariableValueFromProvider( { // First get the provider for the path. ProviderInfo providerInfo = null; - string unused = - this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); + _ = this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); ProviderInvocationException providerException = new ProviderInvocationException( @@ -741,8 +737,7 @@ internal object GetVariableValueAtScope(string name, string scopeID) // First get the provider for the path. ProviderInfo providerInfo = null; - string unused = - this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); + _ = this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); throw NewProviderInvocationException( "ProviderCannotBeUsedAsVariable", @@ -757,8 +752,7 @@ internal object GetVariableValueAtScope(string name, string scopeID) // First get the provider for the path. ProviderInfo providerInfo = null; - string unused = - this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); + _ = this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); throw NewProviderInvocationException( "ProviderCannotBeUsedAsVariable", @@ -796,8 +790,7 @@ internal object GetVariableValueAtScope(string name, string scopeID) // First get the provider for the path. ProviderInfo providerInfo = null; - string unused = - this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); + _ = this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); throw NewProviderInvocationException( "ProviderVariableSyntaxInvalid", @@ -835,8 +828,7 @@ internal object GetVariableValueAtScope(string name, string scopeID) // First get the provider for the path. ProviderInfo providerInfo = null; - string unused = - this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); + _ = this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); ProviderInvocationException providerException = new ProviderInvocationException( @@ -1245,8 +1237,7 @@ internal object SetVariable( // First get the provider for the path. ProviderInfo providerInfo = null; - string unused = - this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); + _ = this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); throw NewProviderInvocationException( "ProviderCannotBeUsedAsVariable", @@ -1261,8 +1252,7 @@ internal object SetVariable( // First get the provider for the path. ProviderInfo providerInfo = null; - string unused = - this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); + _ = this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); throw NewProviderInvocationException( "ProviderCannotBeUsedAsVariable", @@ -1301,8 +1291,7 @@ internal object SetVariable( // First get the provider for the path. ProviderInfo providerInfo = null; - string unused = - this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); + _ = this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); throw NewProviderInvocationException( "ProviderVariableSyntaxInvalid", @@ -1325,8 +1314,7 @@ internal object SetVariable( // First get the provider for the path. ProviderInfo providerInfo = null; - string unused = - this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); + _ = this.Globber.GetProviderPath(variablePath.QualifiedName, out providerInfo); ProviderInvocationException providerException = new ProviderInvocationException( @@ -1839,10 +1827,7 @@ private static void GetScopeVariableTable(SessionStateScope scope, Dictionary diff --git a/src/System.Management.Automation/engine/ShellVariable.cs b/src/System.Management.Automation/engine/ShellVariable.cs index 424be295346..8440845a32d 100644 --- a/src/System.Management.Automation/engine/ShellVariable.cs +++ b/src/System.Management.Automation/engine/ShellVariable.cs @@ -244,6 +244,14 @@ internal void DebuggerCheckVariableWrite() } } + /// + /// Gets the value without triggering debugger check. + /// + internal virtual object GetValueRaw() + { + return _value; + } + /// /// Gets or sets the value of the variable. /// @@ -796,6 +804,11 @@ public override object Value } } + internal override object GetValueRaw() + { + return _tuple.GetValue(_tupleSlot); + } + internal override void SetValueRaw(object newValue, bool preserveValueTypeSemantics) { if (preserveValueTypeSemantics) diff --git a/src/System.Management.Automation/engine/SpecialVariables.cs b/src/System.Management.Automation/engine/SpecialVariables.cs index 0f95349664f..d093cdbbb50 100644 --- a/src/System.Management.Automation/engine/SpecialVariables.cs +++ b/src/System.Management.Automation/engine/SpecialVariables.cs @@ -204,6 +204,7 @@ internal static class SpecialVariables internal static readonly VariablePath PSModuleAutoLoadingPreferenceVarPath = new VariablePath("global:" + PSModuleAutoLoading); #region Platform Variables + internal const string IsLinux = "IsLinux"; internal static readonly VariablePath IsLinuxPath = new VariablePath("IsLinux"); @@ -221,6 +222,7 @@ internal static class SpecialVariables internal static readonly VariablePath IsCoreCLRPath = new VariablePath("IsCoreCLR"); #endregion + #region Preference Variables internal const string DebugPreference = "DebugPreference"; @@ -257,6 +259,11 @@ internal static class SpecialVariables #endregion Preference Variables + internal const string PSNativeCommandUseErrorActionPreference = nameof(PSNativeCommandUseErrorActionPreference); + + internal static readonly VariablePath PSNativeCommandUseErrorActionPreferenceVarPath = + new(PSNativeCommandUseErrorActionPreference); + // Native command argument passing style internal const string NativeArgumentPassing = "PSNativeCommandArgumentPassing"; @@ -321,25 +328,30 @@ internal static class SpecialVariables /* PSCommandPath */ typeof(string), }; - internal static readonly string[] PreferenceVariables = { - SpecialVariables.DebugPreference, - SpecialVariables.VerbosePreference, - SpecialVariables.ErrorActionPreference, - SpecialVariables.WhatIfPreference, - SpecialVariables.WarningPreference, - SpecialVariables.InformationPreference, - SpecialVariables.ConfirmPreference, - }; - - internal static readonly Type[] PreferenceVariableTypes = { - /* DebugPreference */ typeof(ActionPreference), - /* VerbosePreference */ typeof(ActionPreference), - /* ErrorPreference */ typeof(ActionPreference), - /* WhatIfPreference */ typeof(SwitchParameter), - /* WarningPreference */ typeof(ActionPreference), - /* InformationPreference */ typeof(ActionPreference), - /* ConfirmPreference */ typeof(ConfirmImpact), - }; + // This array and the one below it exist to optimize the way common parameters work in advanced functions. + // Common parameters work by setting preference variables in the scope of the function and restoring the old value afterward. + // Variables that don't correspond to common cmdlet parameters don't need to be added here. + internal static readonly string[] PreferenceVariables = + { + SpecialVariables.DebugPreference, + SpecialVariables.VerbosePreference, + SpecialVariables.ErrorActionPreference, + SpecialVariables.WhatIfPreference, + SpecialVariables.WarningPreference, + SpecialVariables.InformationPreference, + SpecialVariables.ConfirmPreference, + }; + + internal static readonly Type[] PreferenceVariableTypes = + { + /* DebugPreference */ typeof(ActionPreference), + /* VerbosePreference */ typeof(ActionPreference), + /* ErrorPreference */ typeof(ActionPreference), + /* WhatIfPreference */ typeof(SwitchParameter), + /* WarningPreference */ typeof(ActionPreference), + /* InformationPreference */ typeof(ActionPreference), + /* ConfirmPreference */ typeof(ConfirmImpact), + }; // The following variables are created in every session w/ AllScope. We avoid creating local slots when we // see an assignment to any of these variables so that they get handled properly (either throwing an exception diff --git a/src/System.Management.Automation/engine/TransactedString.cs b/src/System.Management.Automation/engine/TransactedString.cs index 948cf1d9d81..f1bc8f586b4 100644 --- a/src/System.Management.Automation/engine/TransactedString.cs +++ b/src/System.Management.Automation/engine/TransactedString.cs @@ -8,7 +8,7 @@ namespace Microsoft.PowerShell.Commands.Management { /// - /// Represents a a string that can be used in transactions. + /// Represents a string that can be used in transactions. /// public class TransactedString : IEnlistmentNotification { diff --git a/src/System.Management.Automation/engine/TypeMetadata.cs b/src/System.Management.Automation/engine/TypeMetadata.cs index 31a6adeab73..6981640a165 100644 --- a/src/System.Management.Automation/engine/TypeMetadata.cs +++ b/src/System.Management.Automation/engine/TypeMetadata.cs @@ -761,6 +761,7 @@ internal bool IsMatchingType(PSTypeName psTypeName) private const string AliasesFormat = @"{0}[Alias({1})]"; private const string ValidateLengthFormat = @"{0}[ValidateLength({1}, {2})]"; private const string ValidateRangeRangeKindFormat = @"{0}[ValidateRange([System.Management.Automation.ValidateRangeKind]::{1})]"; + private const string ValidateRangeEnumFormat = @"{0}[ValidateRange([{3}]::{1}, [{3}]::{2})]"; private const string ValidateRangeFloatFormat = @"{0}[ValidateRange({1:R}, {2:R})]"; private const string ValidateRangeFormat = @"{0}[ValidateRange({1}, {2})]"; private const string ValidatePatternFormat = "{0}[ValidatePattern('{1}')]"; @@ -931,6 +932,10 @@ private static string GetProxyAttributeData(Attribute attrib, string prefix) { format = ValidateRangeFloatFormat; } + else if (rangeType.IsEnum) + { + format = ValidateRangeEnumFormat; + } else { format = ValidateRangeFormat; @@ -941,7 +946,8 @@ private static string GetProxyAttributeData(Attribute attrib, string prefix) format, prefix, validRangeAttrib.MinRange, - validRangeAttrib.MaxRange); + validRangeAttrib.MaxRange, + rangeType.FullName); return result; } } diff --git a/src/System.Management.Automation/engine/TypeTable.cs b/src/System.Management.Automation/engine/TypeTable.cs index 0bcd276a45b..5e41aec1eb6 100644 --- a/src/System.Management.Automation/engine/TypeTable.cs +++ b/src/System.Management.Automation/engine/TypeTable.cs @@ -139,17 +139,17 @@ private void UnknownNode(string node, string expectedNodes) else { _context.AddError(_readerLineInfo.LineNumber, TypesXmlStrings.UnknownNode, _reader.LocalName, expectedNodes); - SkipUntillNodeEnd(_reader.LocalName); + SkipUntilNodeEnd(_reader.LocalName); } } - private void SkipUntillNodeEnd(string nodeName) + private void SkipUntilNodeEnd(string nodeName) { while (_reader.Read()) { if (_reader.IsStartElement() && _reader.LocalName.Equals(nodeName)) { - SkipUntillNodeEnd(nodeName); + SkipUntilNodeEnd(nodeName); } else if ((_reader.NodeType == XmlNodeType.EndElement) && _reader.LocalName.Equals(nodeName)) { @@ -771,10 +771,7 @@ private MemberSetData Read_MemberSet() } // Somewhat pointlessly (backcompat), we allow a missing Member node - if (members == null) - { - members = new Collection(); - } + members ??= new Collection(); if (_context.errors.Count != errorCount) { @@ -841,10 +838,7 @@ private PropertySetData Read_PropertySet() { if ((object)_reader.LocalName == (object)_idName) { - if (referencedProperties == null) - { - referencedProperties = new List(8); - } + referencedProperties ??= new List(8); referencedProperties.Add(ReadElementString(_idName)); } @@ -1747,7 +1741,7 @@ internal void AddError(string typeName, int errorLineNumber, string resourceStri /// /// This exception is used by TypeTable constructor to indicate errors - /// occured during construction time. + /// occurred during construction time. /// [Serializable] public class TypeTableLoadException : RuntimeException @@ -1796,7 +1790,7 @@ public TypeTableLoadException(string message, Exception innerException) /// time. /// /// - /// The errors that occured + /// The errors that occurred /// internal TypeTableLoadException(ConcurrentBag loadErrors) : base(TypesXmlStrings.TypeTableLoadErrors) @@ -3724,10 +3718,7 @@ private void ProcessTypeDataToAdd(ConcurrentBag errors, TypeData typeDat if (hasStandardMembers) { - if (typeMembers == null) - { - typeMembers = _extendedMembers.GetOrAdd(typeName, GetValueFactoryBasedOnInitCapacity(capacity: 1)); - } + typeMembers ??= _extendedMembers.GetOrAdd(typeName, GetValueFactoryBasedOnInitCapacity(capacity: 1)); ProcessStandardMembers(errors, typeName, typeData.StandardMembers, propertySets, typeMembers, typeData.IsOverride); } @@ -3937,8 +3928,7 @@ internal TypeTable(IEnumerable typeFiles, AuthorizationManager authoriza throw PSTraceSource.NewArgumentException("typeFile", TypesXmlStrings.TypeFileNotRooted, typefile); } - bool unused; - Initialize(string.Empty, typefile, errors, authorizationManager, host, out unused); + Initialize(string.Empty, typefile, errors, authorizationManager, host, out _); _typeFileList.Add(typefile); } diff --git a/src/System.Management.Automation/engine/TypeTable_Types_Ps1Xml.cs b/src/System.Management.Automation/engine/TypeTable_Types_Ps1Xml.cs index 7113c0953cb..5ecd5914904 100644 --- a/src/System.Management.Automation/engine/TypeTable_Types_Ps1Xml.cs +++ b/src/System.Management.Automation/engine/TypeTable_Types_Ps1Xml.cs @@ -676,17 +676,25 @@ private void Process_Types_Ps1Xml(string filePath, ConcurrentBag errors) typeMembers, isOverride: false); - newMembers.Add(@"Target"); + newMembers.Add(@"ResolvedTarget"); AddMember( errors, typeName, new PSCodeProperty( - @"Target", - GetMethodInfo(typeof(Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods), @"GetTarget"), + @"ResolvedTarget", + GetMethodInfo(typeof(Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods), @"ResolvedTarget"), setterCodeReference: null), typeMembers, isOverride: false); + newMembers.Add(@"Target"); + AddMember( + errors, + typeName, + new PSAliasProperty(@"Target", @"LinkTarget", conversionType: null), + typeMembers, + isOverride: false); + newMembers.Add(@"LinkType"); AddMember( errors, @@ -804,17 +812,25 @@ private void Process_Types_Ps1Xml(string filePath, ConcurrentBag errors) typeMembers, isOverride: false); - newMembers.Add(@"Target"); + newMembers.Add(@"ResolvedTarget"); AddMember( errors, typeName, new PSCodeProperty( - @"Target", - GetMethodInfo(typeof(Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods), @"GetTarget"), + @"ResolvedTarget", + GetMethodInfo(typeof(Microsoft.PowerShell.Commands.InternalSymbolicLinkLinkCodeMethods), @"ResolvedTarget"), setterCodeReference: null), typeMembers, isOverride: false); + newMembers.Add(@"Target"); + AddMember( + errors, + typeName, + new PSAliasProperty(@"Target", @"LinkTarget", conversionType: null), + typeMembers, + isOverride: false); + newMembers.Add(@"LinkType"); AddMember( errors, @@ -4065,152 +4081,6 @@ private void Process_Types_Ps1Xml(string filePath, ConcurrentBag errors) #endregion System.Management.ManagementObject - #region System.Security.AccessControl.ObjectSecurity - - typeName = @"System.Security.AccessControl.ObjectSecurity"; - typeMembers = _extendedMembers.GetOrAdd(typeName, static key => new PSMemberInfoInternalCollection(capacity: 7)); - Type securityDescriptorCommandsBaseType = TypeResolver.ResolveType("Microsoft.PowerShell.Commands.SecurityDescriptorCommandsBase", exception: out _); - - // Process regular members. - newMembers.Add(@"Path"); - AddMember( - errors, - typeName, - new PSCodeProperty( - @"Path", - GetMethodInfo(securityDescriptorCommandsBaseType, @"GetPath"), - setterCodeReference: null), - typeMembers, - isOverride: false); - - newMembers.Add(@"Owner"); - AddMember( - errors, - typeName, - new PSCodeProperty( - @"Owner", - GetMethodInfo(securityDescriptorCommandsBaseType, @"GetOwner"), - setterCodeReference: null), - typeMembers, - isOverride: false); - - newMembers.Add(@"Group"); - AddMember( - errors, - typeName, - new PSCodeProperty( - @"Group", - GetMethodInfo(securityDescriptorCommandsBaseType, @"GetGroup"), - setterCodeReference: null), - typeMembers, - isOverride: false); - - newMembers.Add(@"Access"); - AddMember( - errors, - typeName, - new PSCodeProperty( - @"Access", - GetMethodInfo(securityDescriptorCommandsBaseType, @"GetAccess"), - setterCodeReference: null), - typeMembers, - isOverride: false); - - newMembers.Add(@"Sddl"); - AddMember( - errors, - typeName, - new PSCodeProperty( - @"Sddl", - GetMethodInfo(securityDescriptorCommandsBaseType, @"GetSddl"), - setterCodeReference: null), - typeMembers, - isOverride: false); - - newMembers.Add(@"AccessToString"); - AddMember( - errors, - typeName, - new PSScriptProperty( - @"AccessToString", - GetScriptBlock(@"$toString = """"; - $first = $true; - if ( ! $this.Access ) { return """" } - - foreach($ace in $this.Access) - { - if($first) - { - $first = $false; - } - else - { - $tostring += ""`n""; - } - - $toString += $ace.IdentityReference.ToString(); - $toString += "" ""; - $toString += $ace.AccessControlType.ToString(); - $toString += "" ""; - if($ace -is [System.Security.AccessControl.FileSystemAccessRule]) - { - $toString += $ace.FileSystemRights.ToString(); - } - elseif($ace -is [System.Security.AccessControl.RegistryAccessRule]) - { - $toString += $ace.RegistryRights.ToString(); - } - } - - return $toString;"), - setterScript: null, - shouldCloneOnAccess: true), - typeMembers, - isOverride: false); - - newMembers.Add(@"AuditToString"); - AddMember( - errors, - typeName, - new PSScriptProperty( - @"AuditToString", - GetScriptBlock(@"$toString = """"; - $first = $true; - if ( ! (& { Set-StrictMode -Version 1; $this.audit }) ) { return """" } - - foreach($ace in (& { Set-StrictMode -Version 1; $this.audit })) - { - if($first) - { - $first = $false; - } - else - { - $tostring += ""`n""; - } - - $toString += $ace.IdentityReference.ToString(); - $toString += "" ""; - $toString += $ace.AuditFlags.ToString(); - $toString += "" ""; - if($ace -is [System.Security.AccessControl.FileSystemAuditRule]) - { - $toString += $ace.FileSystemRights.ToString(); - } - elseif($ace -is [System.Security.AccessControl.RegistryAuditRule]) - { - $toString += $ace.RegistryRights.ToString(); - } - } - - return $toString;"), - setterScript: null, - shouldCloneOnAccess: true), - typeMembers, - isOverride: false); - - #endregion System.Security.AccessControl.ObjectSecurity - #region Microsoft.PowerShell.Commands.HistoryInfo typeName = @"Microsoft.PowerShell.Commands.HistoryInfo"; diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index f277dcb1e49..331949d2f0f 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -574,10 +574,7 @@ internal static bool IsWinPEHost() catch (ObjectDisposedException) { } finally { - if (winPEKey != null) - { - winPEKey.Dispose(); - } + winPEKey?.Dispose(); } #endif return false; @@ -1067,10 +1064,7 @@ internal static void EnsureModuleLoaded(string module, ExecutionContext context) finally { context.AutoLoadingModuleInProgress.Remove(module); - if (ps != null) - { - ps.Dispose(); - } + ps?.Dispose(); } } } @@ -1127,10 +1121,7 @@ internal static List GetModules(string module, ExecutionContext co } finally { - if (ps != null) - { - ps.Dispose(); - } + ps?.Dispose(); } return result; @@ -1188,10 +1179,7 @@ internal static List GetModules(ModuleSpecification fullyQualified } finally { - if (ps != null) - { - ps.Dispose(); - } + ps?.Dispose(); } return result; @@ -1281,7 +1269,7 @@ internal static bool IsReservedDeviceName(string destinationPath) return false; } - internal static bool PathIsUnc(string path) + internal static bool PathIsUnc(string path, bool networkOnly = false) { #if UNIX return false; @@ -1291,8 +1279,8 @@ internal static bool PathIsUnc(string path) return false; } - // handle special cases like \\wsl$\ubuntu which isn't a UNC path, but we can say it is so the filesystemprovider can use it - if (path.StartsWith(WslRootPath, StringComparison.OrdinalIgnoreCase)) + // handle special cases like '\\wsl$\ubuntu', '\\?\', and '\\.\pipe\' which aren't a UNC path, but we can say it is so the filesystemprovider can use it + if (!networkOnly && (path.StartsWith(WslRootPath, StringComparison.OrdinalIgnoreCase) || path.StartsWith("\\\\?\\") || path.StartsWith("\\\\.\\"))) { return true; } @@ -1574,7 +1562,6 @@ public static class InternalTestHooks internal static bool BypassAppLockerPolicyCaching; internal static bool BypassOnlineHelpRetrieval; internal static bool ForcePromptForChoiceDefaultOption; - internal static bool BypassOutputRedirectionCheck; internal static bool NoPromptForPassword; internal static bool ForceFormatListFixedLabelWidth; @@ -1593,6 +1580,8 @@ public static class InternalTestHooks internal static bool SetConsoleWidthToZero; internal static bool SetConsoleHeightToZero; + internal static bool SetDate; + // A location to test PSEdition compatibility functionality for Windows PowerShell modules with // since we can't manipulate the System32 directory in a test internal static string TestWindowsPowerShellPSHomeLocation; @@ -1616,10 +1605,7 @@ public static class InternalTestHooks public static void SetTestHook(string property, object value) { var fieldInfo = typeof(InternalTestHooks).GetField(property, BindingFlags.Static | BindingFlags.NonPublic); - if (fieldInfo != null) - { - fieldInfo.SetValue(null, value); - } + fieldInfo?.SetValue(null, value); } /// diff --git a/src/System.Management.Automation/engine/cmdlet.cs b/src/System.Management.Automation/engine/cmdlet.cs index ba7548f982d..953156873fa 100644 --- a/src/System.Management.Automation/engine/cmdlet.cs +++ b/src/System.Management.Automation/engine/cmdlet.cs @@ -801,7 +801,7 @@ public void WriteInformation(InformationRecord informationRecord) /// , /// /// - /// + /// /// namespace Microsoft.Samples.MSH.Cmdlet /// { /// [Cmdlet(VerbsCommon.Remove,"myobjecttype1")] @@ -824,7 +824,7 @@ public void WriteInformation(InformationRecord informationRecord) /// } /// } /// } - /// + /// /// /// /// @@ -897,7 +897,7 @@ public bool ShouldProcess(string target) /// , /// /// - /// + /// /// namespace Microsoft.Samples.MSH.Cmdlet /// { /// [Cmdlet(VerbsCommon.Remove,"myobjecttype2")] @@ -920,7 +920,7 @@ public bool ShouldProcess(string target) /// } /// } /// } - /// + /// /// /// /// @@ -1001,7 +1001,7 @@ public bool ShouldProcess(string target, string action) /// , /// /// - /// + /// /// namespace Microsoft.Samples.MSH.Cmdlet /// { /// [Cmdlet(VerbsCommon.Remove,"myobjecttype3")] @@ -1027,7 +1027,7 @@ public bool ShouldProcess(string target, string action) /// } /// } /// } - /// + /// /// /// /// @@ -1117,7 +1117,7 @@ public bool ShouldProcess( /// , /// /// - /// + /// /// namespace Microsoft.Samples.MSH.Cmdlet /// { /// [Cmdlet(VerbsCommon.Remove,"myobjecttype3")] @@ -1145,7 +1145,7 @@ public bool ShouldProcess( /// } /// } /// } - /// + /// /// /// /// @@ -1233,7 +1233,7 @@ public bool ShouldProcess( /// to ShouldProcess for the Cmdlet instance. /// /// - /// + /// /// namespace Microsoft.Samples.MSH.Cmdlet /// { /// [Cmdlet(VerbsCommon.Remove,"myobjecttype4")] @@ -1277,7 +1277,7 @@ public bool ShouldProcess( /// } /// } /// } - /// + /// /// /// /// @@ -1362,7 +1362,7 @@ public bool ShouldContinue(string query, string caption) /// to ShouldProcess for the Cmdlet instance. /// /// - /// + /// /// namespace Microsoft.Samples.MSH.Cmdlet /// { /// [Cmdlet(VerbsCommon.Remove,"myobjecttype4")] @@ -1411,7 +1411,7 @@ public bool ShouldContinue(string query, string caption) /// } /// } /// } - /// + /// /// /// /// @@ -1502,7 +1502,7 @@ public bool ShouldContinue( /// to ShouldProcess for the Cmdlet instance. /// /// - /// + /// /// namespace Microsoft.Samples.MSH.Cmdlet /// { /// [Cmdlet(VerbsCommon.Remove,"myobjecttype4")] @@ -1551,7 +1551,7 @@ public bool ShouldContinue( /// } /// } /// } - /// + /// /// /// /// diff --git a/src/System.Management.Automation/engine/debugger/Breakpoint.cs b/src/System.Management.Automation/engine/debugger/Breakpoint.cs index 5daf3bdc18b..e51d79f4afa 100644 --- a/src/System.Management.Automation/engine/debugger/Breakpoint.cs +++ b/src/System.Management.Automation/engine/debugger/Breakpoint.cs @@ -531,15 +531,16 @@ internal bool TrySetBreakpoint(string scriptFile, FunctionContext functionContex // Not found. First, we check if the line/column is before any real code. If so, we'll // move the breakpoint to the first interesting sequence point (could be a dynamicparam, - // begin, process, or end block.) + // begin, process, end, or clean block.) if (scriptBlock != null) { var ast = scriptBlock.Ast; var bodyAst = ((IParameterMetadataProvider)ast).Body; - if ((bodyAst.DynamicParamBlock == null || bodyAst.DynamicParamBlock.Extent.IsAfter(Line, Column)) && - (bodyAst.BeginBlock == null || bodyAst.BeginBlock.Extent.IsAfter(Line, Column)) && - (bodyAst.ProcessBlock == null || bodyAst.ProcessBlock.Extent.IsAfter(Line, Column)) && - (bodyAst.EndBlock == null || bodyAst.EndBlock.Extent.IsAfter(Line, Column))) + if ((bodyAst.DynamicParamBlock == null || bodyAst.DynamicParamBlock.Extent.IsAfter(Line, Column)) + && (bodyAst.BeginBlock == null || bodyAst.BeginBlock.Extent.IsAfter(Line, Column)) + && (bodyAst.ProcessBlock == null || bodyAst.ProcessBlock.Extent.IsAfter(Line, Column)) + && (bodyAst.EndBlock == null || bodyAst.EndBlock.Extent.IsAfter(Line, Column)) + && (bodyAst.CleanBlock == null || bodyAst.CleanBlock.Extent.IsAfter(Line, Column))) { SetBreakpoint(functionContext, 0); return true; diff --git a/src/System.Management.Automation/engine/debugger/debugger.cs b/src/System.Management.Automation/engine/debugger/debugger.cs index e8e8c849442..149fca2a632 100644 --- a/src/System.Management.Automation/engine/debugger/debugger.cs +++ b/src/System.Management.Automation/engine/debugger/debugger.cs @@ -1057,12 +1057,9 @@ private bool IsLocalSession { get { - if (_isLocalSession == null) - { - // Remote debug sessions always have a ServerRemoteHost. Otherwise it is a local session. - _isLocalSession = !(((_context.InternalHost.ExternalHost != null) && - (_context.InternalHost.ExternalHost is System.Management.Automation.Remoting.ServerRemoteHost))); - } + // Remote debug sessions always have a ServerRemoteHost. Otherwise it is a local session. + _isLocalSession ??= !((_context.InternalHost.ExternalHost != null) && + (_context.InternalHost.ExternalHost is System.Management.Automation.Remoting.ServerRemoteHost)); return _isLocalSession.Value; } @@ -1949,10 +1946,7 @@ private bool WaitForDebugStopSubscriber() if (_preserveUnhandledDebugStopEvent) { // Lazily create the event object. - if (_preserveDebugStopEvent == null) - { - _preserveDebugStopEvent = new ManualResetEventSlim(true); - } + _preserveDebugStopEvent ??= new ManualResetEventSlim(true); // Set the event handle to non-signaled. if (!_preserveDebugStopEvent.IsSet) @@ -2152,11 +2146,8 @@ private bool IsSystemLockedDown { lock (_syncObject) { - if (_isSystemLockedDown == null) - { - _isSystemLockedDown = (System.Management.Automation.Security.SystemPolicy.GetSystemLockdownPolicy() == - System.Management.Automation.Security.SystemEnforcementMode.Enforce); - } + _isSystemLockedDown ??= (System.Management.Automation.Security.SystemPolicy.GetSystemLockdownPolicy() == + System.Management.Automation.Security.SystemEnforcementMode.Enforce); } } @@ -2395,10 +2386,7 @@ public override void StopProcessCommand() } PowerShell ps = _psDebuggerCommand; - if (ps != null) - { - ps.BeginStop(null, null); - } + ps?.BeginStop(null, null); } /// @@ -3249,11 +3237,7 @@ private void SetRunspaceListToStep(bool enableStepping) try { Debugger nestedDebugger = item.NestedDebugger; - - if (nestedDebugger != null) - { - nestedDebugger.SetDebuggerStepMode(enableStepping); - } + nestedDebugger?.SetDebuggerStepMode(enableStepping); } catch (PSNotImplementedException) { } } @@ -4545,10 +4529,7 @@ internal void CheckStateAndRaiseStopEvent() // If this is a remote server debugger then we want to convert the pending remote // debugger stop to a local debugger stop event for this Debug-Runspace to handle. ServerRemoteDebugger serverRemoteDebugger = this._wrappedDebugger as ServerRemoteDebugger; - if (serverRemoteDebugger != null) - { - serverRemoteDebugger.ReleaseAndRaiseDebugStopLocal(); - } + serverRemoteDebugger?.ReleaseAndRaiseDebugStopLocal(); } } @@ -4960,10 +4941,7 @@ private static void RestoreRemoteOutput(object runningCmd) else { Pipeline pipelineCommand = runningCmd as Pipeline; - if (pipelineCommand != null) - { - pipelineCommand.ResumeIncomingData(); - } + pipelineCommand?.ResumeIncomingData(); } } @@ -5338,47 +5316,29 @@ private void DisplayScript(PSHost host, IList output, InvocationInfo i private static void WriteLine(string line, PSHost host, IList output) { - if (host != null) - { - host.UI.WriteLine(line); - } + host?.UI.WriteLine(line); - if (output != null) - { - output.Add(new PSObject(line)); - } + output?.Add(new PSObject(line)); } private static void WriteCR(PSHost host, IList output) { - if (host != null) - { - host.UI.WriteLine(); - } + host?.UI.WriteLine(); - if (output != null) - { - output.Add(new PSObject(Crlf)); - } + output?.Add(new PSObject(Crlf)); } private static void WriteErrorLine(string error, PSHost host, IList output) { - if (host != null) - { - host.UI.WriteErrorLine(error); - } + host?.UI.WriteErrorLine(error); - if (output != null) - { - output.Add( - new PSObject( - new ErrorRecord( - new RuntimeException(error), - "DebuggerError", - ErrorCategory.InvalidOperation, - null))); - } + output?.Add( + new PSObject( + new ErrorRecord( + new RuntimeException(error), + "DebuggerError", + ErrorCategory.InvalidOperation, + null))); } } diff --git a/src/System.Management.Automation/engine/hostifaces/AsyncResult.cs b/src/System.Management.Automation/engine/hostifaces/AsyncResult.cs index f1e57e6fe39..74bcefae2a8 100644 --- a/src/System.Management.Automation/engine/hostifaces/AsyncResult.cs +++ b/src/System.Management.Automation/engine/hostifaces/AsyncResult.cs @@ -16,7 +16,7 @@ internal class AsyncResult : IAsyncResult #region Private Data private ManualResetEvent _completedWaitHandle; - // exception occured in the async thread. + // exception occurred in the async thread. // user supplied state object // Invoke on thread (remote debugging support). @@ -85,10 +85,7 @@ public WaitHandle AsyncWaitHandle { lock (SyncObject) { - if (_completedWaitHandle == null) - { - _completedWaitHandle = new ManualResetEvent(IsCompleted); - } + _completedWaitHandle ??= new ManualResetEvent(IsCompleted); } } @@ -125,7 +122,7 @@ public WaitHandle AsyncWaitHandle /// Marks the async operation as completed. /// /// - /// Exception occured. null if no exception occured + /// Exception occurred. null if no exception occurred /// internal void SetAsCompleted(Exception exception) { @@ -178,10 +175,7 @@ internal void SignalWaitHandle() { lock (SyncObject) { - if (_completedWaitHandle != null) - { - _completedWaitHandle.Set(); - } + _completedWaitHandle?.Set(); } } @@ -222,7 +216,7 @@ internal void EndInvoke() _invokeOnThreadEvent.Dispose(); _invokeOnThreadEvent = null; // Allow early GC - // Operation is done: if an exception occured, throw it + // Operation is done: if an exception occurred, throw it if (Exception != null) { throw Exception; diff --git a/src/System.Management.Automation/engine/hostifaces/Connection.cs b/src/System.Management.Automation/engine/hostifaces/Connection.cs index 5af011adb82..e892631d0d1 100644 --- a/src/System.Management.Automation/engine/hostifaces/Connection.cs +++ b/src/System.Management.Automation/engine/hostifaces/Connection.cs @@ -414,7 +414,7 @@ internal RunspaceAvailabilityEventArgs(RunspaceAvailability runspaceAvailability public enum RunspaceCapability { /// - /// No additional capabilities beyond a default runspace. + /// Legacy capabilities for WinRM only, from Win7 timeframe. /// Default = 0x0, @@ -436,7 +436,12 @@ public enum RunspaceCapability /// /// Runspace is based on SSH transport. /// - SSHTransport = 0x8 + SSHTransport = 0x8, + + /// + /// Runspace is based on open custom connection/transport support. + /// + CustomTransport = 0x100 } #endregion diff --git a/src/System.Management.Automation/engine/hostifaces/ConnectionBase.cs b/src/System.Management.Automation/engine/hostifaces/ConnectionBase.cs index b6b571e90e7..ca3847cc593 100644 --- a/src/System.Management.Automation/engine/hostifaces/ConnectionBase.cs +++ b/src/System.Management.Automation/engine/hostifaces/ConnectionBase.cs @@ -661,7 +661,7 @@ protected RunspaceState RunspaceState } /// - /// This is queue of all the state change event which have occured for + /// This is queue of all the state change event which have occurred for /// this runspace. RaiseRunspaceStateEvents raises event for each /// item in this queue. We don't raise events from with SetRunspaceState /// because SetRunspaceState is often called from with in the a lock. diff --git a/src/System.Management.Automation/engine/hostifaces/ConnectionFactory.cs b/src/System.Management.Automation/engine/hostifaces/ConnectionFactory.cs index b812e8fe82b..014eed0f548 100644 --- a/src/System.Management.Automation/engine/hostifaces/ConnectionFactory.cs +++ b/src/System.Management.Automation/engine/hostifaces/ConnectionFactory.cs @@ -539,16 +539,6 @@ public static Runspace CreateRunspace(RunspaceConnectionInfo connectionInfo, PSH /// public static Runspace CreateRunspace(RunspaceConnectionInfo connectionInfo, PSHost host, TypeTable typeTable, PSPrimitiveDictionary applicationArguments, string name) { - if (connectionInfo is not WSManConnectionInfo && - connectionInfo is not NewProcessConnectionInfo && - connectionInfo is not NamedPipeConnectionInfo && - connectionInfo is not SSHConnectionInfo && - connectionInfo is not VMConnectionInfo && - connectionInfo is not ContainerConnectionInfo) - { - throw new NotSupportedException(); - } - if (connectionInfo is WSManConnectionInfo) { RemotingCommandUtil.CheckHostRemotingPrerequisites(); diff --git a/src/System.Management.Automation/engine/hostifaces/History.cs b/src/System.Management.Automation/engine/hostifaces/History.cs index dc74a46213f..3fe2acb19c5 100644 --- a/src/System.Management.Automation/engine/hostifaces/History.cs +++ b/src/System.Management.Automation/engine/hostifaces/History.cs @@ -1471,7 +1471,7 @@ void ProcessRecord() ); } while (false); - // If we are here, an error has occured. + // If we are here, an error has occurred. Exception ex = new InvalidDataException ( diff --git a/src/System.Management.Automation/engine/hostifaces/InternalHostUserInterface.cs b/src/System.Management.Automation/engine/hostifaces/InternalHostUserInterface.cs index 57f3f675ffc..1e820196ca9 100644 --- a/src/System.Management.Automation/engine/hostifaces/InternalHostUserInterface.cs +++ b/src/System.Management.Automation/engine/hostifaces/InternalHostUserInterface.cs @@ -338,13 +338,7 @@ internal void WriteDebugRecord(DebugRecord record) /// Writes the DebugRecord to informational buffers. /// /// DebugRecord. - internal void WriteDebugInfoBuffers(DebugRecord record) - { - if (_informationalBuffers != null) - { - _informationalBuffers.AddDebug(record); - } - } + internal void WriteDebugInfoBuffers(DebugRecord record) => _informationalBuffers?.AddDebug(record); /// /// Helper function for WriteDebugLine. @@ -538,10 +532,7 @@ public override } // Write to Information Buffers - if (_informationalBuffers != null) - { - _informationalBuffers.AddProgress(record); - } + _informationalBuffers?.AddProgress(record); if (_externalUI == null) { @@ -588,13 +579,7 @@ internal void WriteVerboseRecord(VerboseRecord record) /// Writes the VerboseRecord to informational buffers. /// /// VerboseRecord. - internal void WriteVerboseInfoBuffers(VerboseRecord record) - { - if (_informationalBuffers != null) - { - _informationalBuffers.AddVerbose(record); - } - } + internal void WriteVerboseInfoBuffers(VerboseRecord record) => _informationalBuffers?.AddVerbose(record); /// /// See base class. @@ -631,13 +616,7 @@ internal void WriteWarningRecord(WarningRecord record) /// Writes the WarningRecord to informational buffers. /// /// WarningRecord. - internal void WriteWarningInfoBuffers(WarningRecord record) - { - if (_informationalBuffers != null) - { - _informationalBuffers.AddWarning(record); - } - } + internal void WriteWarningInfoBuffers(WarningRecord record) => _informationalBuffers?.AddWarning(record); /// /// @@ -657,13 +636,7 @@ internal void WriteInformationRecord(InformationRecord record) /// Writes the InformationRecord to informational buffers. /// /// WarningRecord. - internal void WriteInformationInfoBuffers(InformationRecord record) - { - if (_informationalBuffers != null) - { - _informationalBuffers.AddInformation(record); - } - } + internal void WriteInformationInfoBuffers(InformationRecord record) => _informationalBuffers?.AddInformation(record); internal static Type GetFieldType(FieldDescription field) { diff --git a/src/System.Management.Automation/engine/hostifaces/LocalConnection.cs b/src/System.Management.Automation/engine/hostifaces/LocalConnection.cs index 7f10b75525c..e640e6f112a 100644 --- a/src/System.Management.Automation/engine/hostifaces/LocalConnection.cs +++ b/src/System.Management.Automation/engine/hostifaces/LocalConnection.cs @@ -84,10 +84,7 @@ public override PSPrimitiveDictionary GetApplicationPrivateData() { lock (this.SyncRoot) { - if (_applicationPrivateData == null) - { - _applicationPrivateData = new PSPrimitiveDictionary(); - } + _applicationPrivateData ??= new PSPrimitiveDictionary(); } } @@ -363,7 +360,7 @@ public override Debugger Debugger } } - private static readonly string s_debugPreferenceCachePath = Path.Combine(Path.Combine(Platform.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "WindowsPowerShell"), "DebugPreference.clixml"); + private static readonly string s_debugPreferenceCachePath = Path.Combine(Platform.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "WindowsPowerShell", "DebugPreference.clixml"); private static readonly object s_debugPreferenceLockObject = new object(); /// @@ -768,10 +765,7 @@ internal void LogEngineHealthEvent(Exception exception, /// internal PipelineThread GetPipelineThread() { - if (_pipelineThread == null) - { - _pipelineThread = new PipelineThread(this.ApartmentState); - } + _pipelineThread ??= new PipelineThread(this.ApartmentState); return _pipelineThread; } @@ -840,18 +834,14 @@ private void DoCloseHelper() if (executionContext != null) { PSHostUserInterface hostUI = executionContext.EngineHostInterface.UI; - if (hostUI != null) - { - hostUI.StopAllTranscribing(); - } + hostUI?.StopAllTranscribing(); } AmsiUtils.Uninitialize(); } // Generate the shutdown event - if (Events != null) - Events.GenerateEvent(PSEngineEvent.Exiting, null, Array.Empty(), null, true, false); + Events?.GenerateEvent(PSEngineEvent.Exiting, null, Array.Empty(), null, true, false); // Stop all running pipelines // Note:Do not perform the Cancel in lock. Reason is @@ -884,7 +874,7 @@ private void DoCloseHelper() return runspaces; }); - // Notify Engine components that that runspace is closing. + // Notify Engine components that runspace is closing. _engine.Context.RunspaceClosingNotification(); // Log engine lifecycle event. @@ -1242,8 +1232,6 @@ protected override void Dispose(bool disposing) RunspaceOpening = null; } - Platform.RemoveTemporaryDirectory(); - // Dispose the event manager if (this.ExecutionContext != null && this.ExecutionContext.Events != null) { @@ -1274,10 +1262,7 @@ public override void Close() base.Close(); // call base.Close() first to make it stop the pipeline - if (_pipelineThread != null) - { - _pipelineThread.Close(); - } + _pipelineThread?.Close(); } #endregion IDisposable Members diff --git a/src/System.Management.Automation/engine/hostifaces/LocalPipeline.cs b/src/System.Management.Automation/engine/hostifaces/LocalPipeline.cs index 5392c1781e1..f77fda65f1f 100644 --- a/src/System.Management.Automation/engine/hostifaces/LocalPipeline.cs +++ b/src/System.Management.Automation/engine/hostifaces/LocalPipeline.cs @@ -487,10 +487,7 @@ private FlowControlException InvokeHelper() } PSLocalEventManager eventManager = LocalRunspace.Events as PSLocalEventManager; - if (eventManager != null) - { - eventManager.ProcessPendingActions(); - } + eventManager?.ProcessPendingActions(); // restore the trap state... this.LocalRunspace.ExecutionContext.PropagateExceptionsToEnclosingStatementBlock = oldTrapState; diff --git a/src/System.Management.Automation/engine/hostifaces/MshHostRawUserInterface.cs b/src/System.Management.Automation/engine/hostifaces/MshHostRawUserInterface.cs index f4c3aedfb69..09d58ea7357 100644 --- a/src/System.Management.Automation/engine/hostifaces/MshHostRawUserInterface.cs +++ b/src/System.Management.Automation/engine/hostifaces/MshHostRawUserInterface.cs @@ -1458,7 +1458,7 @@ public abstract /// Provided for clearing regions -- less chatty than passing an array of cells. /// /// - /// + /// /// using System; /// using System.Management.Automation; /// using System.Management.Automation.Host; @@ -1474,7 +1474,7 @@ public abstract /// } /// } /// } - /// + /// /// /// /// diff --git a/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs b/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs index ea101ade659..0feeb73011f 100644 --- a/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs +++ b/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs @@ -304,18 +304,12 @@ public enum FormatStyle /// /// The format style to get the escape sequence for. /// - /// - /// True if the output is redirected. - /// /// /// The ANSI escape sequence for the given format style. /// - public static string GetFormatStyleString(FormatStyle formatStyle, bool isOutputRedirected) + public static string GetFormatStyleString(FormatStyle formatStyle) { - // redirected console gets plaintext output to preserve existing behavior - if (!InternalTestHooks.BypassOutputRedirectionCheck && - (PSStyle.Instance.OutputRendering == OutputRendering.PlainText || - isOutputRedirected)) + if (PSStyle.Instance.OutputRendering == OutputRendering.PlainText) { return string.Empty; } @@ -353,30 +347,22 @@ public static string GetFormatStyleString(FormatStyle formatStyle, bool isOutput /// /// True if the host supports virtual terminal. /// - /// - /// True if the output is redirected. - /// /// /// The formatted text. /// - public static string GetOutputString(string text, bool supportsVirtualTerminal, bool isOutputRedirected) + public static string GetOutputString(string text, bool supportsVirtualTerminal) { - return GetOutputString(text, isHost: true, supportsVirtualTerminal: supportsVirtualTerminal, isOutputRedirected: isOutputRedirected); + return GetOutputString(text, isHost: true, supportsVirtualTerminal: supportsVirtualTerminal); } - internal static string GetOutputString(string text, bool isHost, bool? supportsVirtualTerminal = null, bool isOutputRedirected = false) + internal static string GetOutputString(string text, bool isHost, bool? supportsVirtualTerminal = null) { var sd = new ValueStringDecorated(text); if (sd.IsDecorated) { var outputRendering = OutputRendering.Ansi; - if (InternalTestHooks.BypassOutputRedirectionCheck) - { - isOutputRedirected = false; - } - - if (isOutputRedirected || ShouldOutputPlainText(isHost, supportsVirtualTerminal)) + if (ShouldOutputPlainText(isHost, supportsVirtualTerminal)) { outputRendering = OutputRendering.PlainText; } @@ -1104,10 +1090,7 @@ internal static TranscriptionOption GetSystemTranscriptOption(TranscriptionOptio // This way, multiple runspaces opened by the same process will share the same transcript. lock (s_systemTranscriptLock) { - if (systemTranscript == null) - { - systemTranscript = PSHostUserInterface.GetTranscriptOptionFromSettings(transcription, currentTranscript); - } + systemTranscript ??= PSHostUserInterface.GetTranscriptOptionFromSettings(transcription, currentTranscript); } } diff --git a/src/System.Management.Automation/engine/hostifaces/NativeCultureResolver.cs b/src/System.Management.Automation/engine/hostifaces/NativeCultureResolver.cs index 74306b822df..1c6779a0f37 100644 --- a/src/System.Management.Automation/engine/hostifaces/NativeCultureResolver.cs +++ b/src/System.Management.Automation/engine/hostifaces/NativeCultureResolver.cs @@ -136,7 +136,7 @@ private VistaCultureInfo ImmediateParent } } - // There is atleast 1 duplicate in m_fallbacks which was not added to + // There is at least 1 duplicate in m_fallbacks which was not added to // fallbacksForTheParent array. Resize the array to take care of this. if (_fallbacks.Length != currentIndex) { diff --git a/src/System.Management.Automation/engine/hostifaces/PSCommand.cs b/src/System.Management.Automation/engine/hostifaces/PSCommand.cs index 32ec57e0899..6db40850381 100644 --- a/src/System.Management.Automation/engine/hostifaces/PSCommand.cs +++ b/src/System.Management.Automation/engine/hostifaces/PSCommand.cs @@ -92,10 +92,7 @@ public PSCommand AddCommand(string command) throw PSTraceSource.NewArgumentNullException(nameof(command)); } - if (_owner != null) - { - _owner.AssertChangesAreAccepted(); - } + _owner?.AssertChangesAreAccepted(); _currentCommand = new Command(command, false); _commands.Add(_currentCommand); @@ -136,10 +133,7 @@ public PSCommand AddCommand(string cmdlet, bool useLocalScope) throw PSTraceSource.NewArgumentNullException(nameof(cmdlet)); } - if (_owner != null) - { - _owner.AssertChangesAreAccepted(); - } + _owner?.AssertChangesAreAccepted(); _currentCommand = new Command(cmdlet, false, useLocalScope); _commands.Add(_currentCommand); @@ -178,10 +172,7 @@ public PSCommand AddScript(string script) throw PSTraceSource.NewArgumentNullException(nameof(script)); } - if (_owner != null) - { - _owner.AssertChangesAreAccepted(); - } + _owner?.AssertChangesAreAccepted(); _currentCommand = new Command(script, true); _commands.Add(_currentCommand); @@ -223,10 +214,7 @@ public PSCommand AddScript(string script, bool useLocalScope) throw PSTraceSource.NewArgumentNullException(nameof(script)); } - if (_owner != null) - { - _owner.AssertChangesAreAccepted(); - } + _owner?.AssertChangesAreAccepted(); _currentCommand = new Command(script, true, useLocalScope); _commands.Add(_currentCommand); @@ -261,10 +249,7 @@ public PSCommand AddCommand(Command command) throw PSTraceSource.NewArgumentNullException(nameof(command)); } - if (_owner != null) - { - _owner.AssertChangesAreAccepted(); - } + _owner?.AssertChangesAreAccepted(); _currentCommand = command; _commands.Add(_currentCommand); @@ -309,10 +294,7 @@ public PSCommand AddParameter(string parameterName, object value) new object[] { "PSCommand" }); } - if (_owner != null) - { - _owner.AssertChangesAreAccepted(); - } + _owner?.AssertChangesAreAccepted(); _currentCommand.Parameters.Add(parameterName, value); return this; @@ -352,10 +334,7 @@ public PSCommand AddParameter(string parameterName) new object[] { "PSCommand" }); } - if (_owner != null) - { - _owner.AssertChangesAreAccepted(); - } + _owner?.AssertChangesAreAccepted(); _currentCommand.Parameters.Add(parameterName, true); return this; @@ -372,10 +351,7 @@ internal PSCommand AddParameter(CommandParameter parameter) new object[] { "PSCommand" }); } - if (_owner != null) - { - _owner.AssertChangesAreAccepted(); - } + _owner?.AssertChangesAreAccepted(); _currentCommand.Parameters.Add(parameter); return this; @@ -415,10 +391,7 @@ public PSCommand AddArgument(object value) new object[] { "PSCommand" }); } - if (_owner != null) - { - _owner.AssertChangesAreAccepted(); - } + _owner?.AssertChangesAreAccepted(); _currentCommand.Parameters.Add(null, value); return this; diff --git a/src/System.Management.Automation/engine/hostifaces/PSDataCollection.cs b/src/System.Management.Automation/engine/hostifaces/PSDataCollection.cs index d51c96a33e0..9f928eca21d 100644 --- a/src/System.Management.Automation/engine/hostifaces/PSDataCollection.cs +++ b/src/System.Management.Automation/engine/hostifaces/PSDataCollection.cs @@ -553,11 +553,8 @@ public void Complete() // raise the events outside of the lock. if (raiseEvents) { - if (_readWaitHandle != null) - { - // unblock any readers waiting on the handle - _readWaitHandle.Set(); - } + // unblock any readers waiting on the handle + _readWaitHandle?.Set(); // A temporary variable is used as the Completed may // reach null (because of -='s) after the null check @@ -798,10 +795,7 @@ public void Clear() { lock (SyncObject) { - if (_data != null) - { - _data.Clear(); - } + _data?.Clear(); } } @@ -1322,12 +1316,9 @@ internal WaitHandle WaitHandle { lock (SyncObject) { - if (_readWaitHandle == null) - { - // Create the handle signaled if there are objects in the buffer - // or the buffer has been closed. - _readWaitHandle = new ManualResetEvent(_data.Count > 0 || !_isOpen); - } + // Create the handle signaled if there are objects in the buffer + // or the buffer has been closed. + _readWaitHandle ??= new ManualResetEvent(_data.Count > 0 || !_isOpen); } } @@ -1522,7 +1513,7 @@ internal void InternalAddRange(Guid psInstanceId, ICollection collection) { InsertItem(psInstanceId, _data.Count, (T)o); - // set raise events if atleast one item is + // set raise events if at least one item is // added. raiseEvents = true; } @@ -1560,10 +1551,7 @@ internal void DecrementRef() if (_refCount != 0 && (!_blockingEnumerator || _refCount != 1)) return; // release threads blocked on waithandle - if (_readWaitHandle != null) - { - _readWaitHandle.Set(); - } + _readWaitHandle?.Set(); // release any threads to notify refCount is 0. Enumerator // blocks on this syncObject and it needs to be notified @@ -1795,10 +1783,7 @@ protected void Dispose(bool disposing) _readWaitHandle = null; } - if (_data != null) - { - _data.Clear(); - } + _data?.Clear(); } } } @@ -2099,65 +2084,35 @@ internal PSDataCollection Debug /// The item is added to the buffer along with PowerShell InstanceId. /// /// - internal void AddProgress(ProgressRecord item) - { - if (progress != null) - { - progress.InternalAdd(_psInstanceId, item); - } - } + internal void AddProgress(ProgressRecord item) => progress?.InternalAdd(_psInstanceId, item); /// /// Adds item to the verbose buffer. /// The item is added to the buffer along with PowerShell InstanceId. /// /// - internal void AddVerbose(VerboseRecord item) - { - if (verbose != null) - { - verbose.InternalAdd(_psInstanceId, item); - } - } + internal void AddVerbose(VerboseRecord item) => verbose?.InternalAdd(_psInstanceId, item); /// /// Adds item to the debug buffer. /// The item is added to the buffer along with PowerShell InstanceId. /// /// - internal void AddDebug(DebugRecord item) - { - if (debug != null) - { - debug.InternalAdd(_psInstanceId, item); - } - } + internal void AddDebug(DebugRecord item) => debug?.InternalAdd(_psInstanceId, item); /// /// Adds item to the warning buffer. /// The item is added to the buffer along with PowerShell InstanceId. /// /// - internal void AddWarning(WarningRecord item) - { - if (Warning != null) - { - Warning.InternalAdd(_psInstanceId, item); - } - } + internal void AddWarning(WarningRecord item) => Warning?.InternalAdd(_psInstanceId, item); /// /// Adds item to the information buffer. /// The item is added to the buffer along with PowerShell InstanceId. /// /// - internal void AddInformation(InformationRecord item) - { - if (Information != null) - { - Information.InternalAdd(_psInstanceId, item); - } - } + internal void AddInformation(InformationRecord item) => Information?.InternalAdd(_psInstanceId, item); #endregion } diff --git a/src/System.Management.Automation/engine/hostifaces/PSTask.cs b/src/System.Management.Automation/engine/hostifaces/PSTask.cs index 6e08733cc2b..fb492706ad1 100644 --- a/src/System.Management.Automation/engine/hostifaces/PSTask.cs +++ b/src/System.Management.Automation/engine/hostifaces/PSTask.cs @@ -496,13 +496,7 @@ public void Start(Runspace runspace) /// /// Signals the running task to stop. /// - public void SignalStop() - { - if (_powershell != null) - { - _powershell.BeginStop(null, null); - } - } + public void SignalStop() => _powershell?.BeginStop(null, null); #endregion } @@ -1536,12 +1530,9 @@ public Debugger Debugger { get { - if (_jobDebuggerWrapper == null) - { - _jobDebuggerWrapper = new PSTaskChildDebugger( - _task.Debugger, - this.Name); - } + _jobDebuggerWrapper ??= new PSTaskChildDebugger( + _task.Debugger, + this.Name); return _jobDebuggerWrapper; } diff --git a/src/System.Management.Automation/engine/hostifaces/Pipeline.cs b/src/System.Management.Automation/engine/hostifaces/Pipeline.cs index 874691852cb..c7b962b7668 100644 --- a/src/System.Management.Automation/engine/hostifaces/Pipeline.cs +++ b/src/System.Management.Automation/engine/hostifaces/Pipeline.cs @@ -445,7 +445,7 @@ internal void SetHadErrors(bool status) /// /// This flag is used to force the redirection. By default it is false to maintain compatibility with /// V1, but the V2 hosting interface (PowerShell class) sets this flag to true to ensure the global - /// error output pipe is always set and $ErrorActionPreference when invoking the Pipeline. + /// error output pipe is always set and $ErrorActionPreference is checked when invoking the Pipeline. /// internal bool RedirectShellErrorOutputPipe { get; set; } = false; diff --git a/src/System.Management.Automation/engine/hostifaces/PowerShell.cs b/src/System.Management.Automation/engine/hostifaces/PowerShell.cs index a2c3c8b4ebe..07d17589c3b 100644 --- a/src/System.Management.Automation/engine/hostifaces/PowerShell.cs +++ b/src/System.Management.Automation/engine/hostifaces/PowerShell.cs @@ -762,10 +762,7 @@ internal void InitForRemotePipeline(CommandCollection command, ObjectStreamBase // create the client remote powershell for remoting // communications - if (RemotePowerShell == null) - { - RemotePowerShell = new ClientRemotePowerShell(this, ((RunspacePool)_rsConnection).RemoteRunspacePoolInternal); - } + RemotePowerShell ??= new ClientRemotePowerShell(this, ((RunspacePool)_rsConnection).RemoteRunspacePoolInternal); // If we get here, we don't call 'Invoke' or any of it's friends on 'this', instead we serialize 'this' in PowerShell.ToPSObjectForRemoting. // Without the following two steps, we'll be missing the 'ExtraCommands' on the serialized instance of 'this'. @@ -804,10 +801,7 @@ internal void InitForRemotePipelineConnect(ObjectStreamBase inputstream, ObjectS RedirectShellErrorOutputPipe = redirectShellErrorOutputPipe; - if (RemotePowerShell == null) - { - RemotePowerShell = new ClientRemotePowerShell(this, ((RunspacePool)_rsConnection).RemoteRunspacePoolInternal); - } + RemotePowerShell ??= new ClientRemotePowerShell(this, ((RunspacePool)_rsConnection).RemoteRunspacePoolInternal); if (!RemotePowerShell.Initialized) { @@ -2236,10 +2230,7 @@ internal void InvokeWithDebugger( { if (addToHistory) { - if (settings == null) - { - settings = new PSInvocationSettings(); - } + settings ??= new PSInvocationSettings(); settings.AddToHistory = true; } @@ -3544,10 +3535,7 @@ private void BatchInvocationCallback(IAsyncResult result) break; } - if (objs == null) - { - objs = _batchAsyncResult.Output; - } + objs ??= _batchAsyncResult.Output; DoRemainingBatchCommands(objs); } @@ -4173,10 +4161,7 @@ private void Dispose(bool disposing) _runspace.Dispose(); } - if (RemotePowerShell != null) - { - RemotePowerShell.Dispose(); - } + RemotePowerShell?.Dispose(); _invokeAsyncResult = null; _stopAsyncResult = null; @@ -4190,10 +4175,7 @@ private void InternalClearSuppressExceptions() { lock (_syncObject) { - if (_worker != null) - { - _worker.InternalClearSuppressExceptions(); - } + _worker?.InternalClearSuppressExceptions(); } } @@ -4319,10 +4301,7 @@ internal void SetStateChanged(PSInvocationStateInfo stateInfo) { if (RunningExtraCommands) { - if (tempInvokeAsyncResult != null) - { - tempInvokeAsyncResult.SetAsCompleted(InvocationStateInfo.Reason); - } + tempInvokeAsyncResult?.SetAsCompleted(InvocationStateInfo.Reason); RaiseStateChangeEvent(InvocationStateInfo.Clone()); } @@ -4330,16 +4309,10 @@ internal void SetStateChanged(PSInvocationStateInfo stateInfo) { RaiseStateChangeEvent(InvocationStateInfo.Clone()); - if (tempInvokeAsyncResult != null) - { - tempInvokeAsyncResult.SetAsCompleted(InvocationStateInfo.Reason); - } + tempInvokeAsyncResult?.SetAsCompleted(InvocationStateInfo.Reason); } - if (tempStopAsyncResult != null) - { - tempStopAsyncResult.SetAsCompleted(null); - } + tempStopAsyncResult?.SetAsCompleted(null); } catch (Exception) { @@ -4351,7 +4324,7 @@ internal void SetStateChanged(PSInvocationStateInfo stateInfo) } finally { - // takes care exception occured with invokeAsyncResult + // takes care exception occurred with invokeAsyncResult if (isExceptionOccured && (tempStopAsyncResult != null)) { tempStopAsyncResult.Release(); @@ -4378,10 +4351,7 @@ internal void SetStateChanged(PSInvocationStateInfo stateInfo) // This object can be disconnected even if "BeginStop" was called if it is a remote object // and robust connections is retrying a failed network connection. // In this case release the stop wait handle to prevent not responding. - if (tempStopAsyncResult != null) - { - tempStopAsyncResult.SetAsCompleted(null); - } + tempStopAsyncResult?.SetAsCompleted(null); // Only raise the Disconnected state changed event if the PowerShell state // actually transitions to Disconnected from some other state. This condition @@ -4402,7 +4372,7 @@ internal void SetStateChanged(PSInvocationStateInfo stateInfo) } finally { - // takes care exception occured with invokeAsyncResult + // takes care exception occurred with invokeAsyncResult if (isExceptionOccured && (tempStopAsyncResult != null)) { tempStopAsyncResult.Release(); @@ -4447,10 +4417,7 @@ internal void ClearRemotePowerShell() { lock (_syncObject) { - if (RemotePowerShell != null) - { - RemotePowerShell.Clear(); - } + RemotePowerShell?.Clear(); } } @@ -5164,11 +5131,8 @@ private IAsyncResult CoreStop(bool isSyncCall, AsyncCallback callback, object st // cannot complete with this object. if (isDisconnected) { - if (_invokeAsyncResult != null) - { - // Since object is stopped, allow result wait to end. - _invokeAsyncResult.SetAsCompleted(null); - } + // Since object is stopped, allow result wait to end. + _invokeAsyncResult?.SetAsCompleted(null); _stopAsyncResult.SetAsCompleted(null); @@ -5229,10 +5193,7 @@ private IAsyncResult CoreStop(bool isSyncCall, AsyncCallback callback, object st private void ReleaseDebugger() { LocalRunspace localRunspace = _runspace as LocalRunspace; - if (localRunspace != null) - { - localRunspace.ReleaseDebugger(); - } + localRunspace?.ReleaseDebugger(); } /// @@ -5333,10 +5294,7 @@ private void AddToRemoteRunspaceRunningList() else { RemoteRunspacePoolInternal remoteRunspacePoolInternal = GetRemoteRunspacePoolInternal(); - if (remoteRunspacePoolInternal != null) - { - remoteRunspacePoolInternal.PushRunningPowerShell(this); - } + remoteRunspacePoolInternal?.PushRunningPowerShell(this); } } @@ -5352,10 +5310,7 @@ private void RemoveFromRemoteRunspaceRunningList() else { RemoteRunspacePoolInternal remoteRunspacePoolInternal = GetRemoteRunspacePoolInternal(); - if (remoteRunspacePoolInternal != null) - { - remoteRunspacePoolInternal.PopRunningPowerShell(); - } + remoteRunspacePoolInternal?.PopRunningPowerShell(); } } @@ -5724,10 +5679,7 @@ internal void InternalClearSuppressExceptions() else { RunspacePool pool = _shell._rsConnection as RunspacePool; - if (pool != null) - { - pool.ReleaseRunspace(CurrentlyRunningPipeline.Runspace); - } + pool?.ReleaseRunspace(CurrentlyRunningPipeline.Runspace); } CurrentlyRunningPipeline.Dispose(); @@ -5898,10 +5850,7 @@ internal void SuspendIncomingData() throw new PSNotSupportedException(); } - if (RemotePowerShell.DataStructureHandler != null) - { - RemotePowerShell.DataStructureHandler.TransportManager.SuspendQueue(true); - } + RemotePowerShell.DataStructureHandler?.TransportManager.SuspendQueue(true); } /// @@ -5914,10 +5863,7 @@ internal void ResumeIncomingData() throw new PSNotSupportedException(); } - if (RemotePowerShell.DataStructureHandler != null) - { - RemotePowerShell.DataStructureHandler.TransportManager.ResumeQueue(); - } + RemotePowerShell.DataStructureHandler?.TransportManager.ResumeQueue(); } /// diff --git a/src/System.Management.Automation/engine/hostifaces/RunspacePoolInternal.cs b/src/System.Management.Automation/engine/hostifaces/RunspacePoolInternal.cs index aac3acfbdf8..0fc328f0d5a 100644 --- a/src/System.Management.Automation/engine/hostifaces/RunspacePoolInternal.cs +++ b/src/System.Management.Automation/engine/hostifaces/RunspacePoolInternal.cs @@ -231,10 +231,7 @@ internal virtual PSPrimitiveDictionary GetApplicationPrivateData() { lock (this.syncObject) { - if (_applicationPrivateData == null) - { - _applicationPrivateData = new PSPrimitiveDictionary(); - } + _applicationPrivateData ??= new PSPrimitiveDictionary(); } } @@ -1306,7 +1303,7 @@ protected void DestroyRunspace(Runspace runspace) /// /// Cleans the pool closing the runspaces that are idle. /// This method is called as part of a timer callback. - /// This method will make sure atleast minPoolSz number + /// This method will make sure at least minPoolSz number /// of Runspaces are active. /// /// diff --git a/src/System.Management.Automation/engine/hostifaces/pipelinebase.cs b/src/System.Management.Automation/engine/hostifaces/pipelinebase.cs index 0eaa30daae0..b4efa770880 100644 --- a/src/System.Management.Automation/engine/hostifaces/pipelinebase.cs +++ b/src/System.Management.Automation/engine/hostifaces/pipelinebase.cs @@ -760,7 +760,7 @@ protected bool IsPipelineFinished() } /// - /// This is queue of all the state change event which have occured for + /// This is queue of all the state change event which have occurred for /// this pipeline. RaisePipelineStateEvents raises event for each /// item in this queue. We don't raise the event with in SetPipelineState /// because often SetPipelineState is called with in a lock. diff --git a/src/System.Management.Automation/engine/interpreter/BranchLabel.cs b/src/System.Management.Automation/engine/interpreter/BranchLabel.cs index 08af974416c..1bae16783a9 100644 --- a/src/System.Management.Automation/engine/interpreter/BranchLabel.cs +++ b/src/System.Management.Automation/engine/interpreter/BranchLabel.cs @@ -110,10 +110,7 @@ internal void AddBranch(InstructionList instructions, int branchIndex) if (_targetIndex == UnknownIndex) { - if (_forwardBranchFixups == null) - { - _forwardBranchFixups = new List(); - } + _forwardBranchFixups ??= new List(); _forwardBranchFixups.Add(branchIndex); } diff --git a/src/System.Management.Automation/engine/interpreter/ControlFlowInstructions.cs b/src/System.Management.Automation/engine/interpreter/ControlFlowInstructions.cs index 0fddd203346..5d994d934c5 100644 --- a/src/System.Management.Automation/engine/interpreter/ControlFlowInstructions.cs +++ b/src/System.Management.Automation/engine/interpreter/ControlFlowInstructions.cs @@ -157,10 +157,7 @@ public override Instruction[] Cache { get { - if (s_caches == null) - { - s_caches = new Instruction[2][][] { new Instruction[2][], new Instruction[2][] }; - } + s_caches ??= new Instruction[2][][] { new Instruction[2][], new Instruction[2][] }; return s_caches[ConsumedStack][ProducedStack] ?? (s_caches[ConsumedStack][ProducedStack] = new Instruction[CacheSize]); } @@ -509,7 +506,7 @@ public override int Run(InterpretedFrame frame) frame.PopPendingContinuation(); // If _pendingContinuation == -1 then we were getting into the finally block because an exception was thrown - // In this case we just return 1, and the the real instruction index will be calculated by GotoHandler later + // In this case we just return 1, and the real instruction index will be calculated by GotoHandler later if (!frame.IsJumpHappened()) { return 1; } // jump to goto target or to the next finally: return frame.YieldToPendingContinuation(); diff --git a/src/System.Management.Automation/engine/interpreter/InstructionFactory.cs b/src/System.Management.Automation/engine/interpreter/InstructionFactory.cs index 4a707201eb6..839ccd6f905 100644 --- a/src/System.Management.Automation/engine/interpreter/InstructionFactory.cs +++ b/src/System.Management.Automation/engine/interpreter/InstructionFactory.cs @@ -119,10 +119,7 @@ protected internal override Instruction NewArrayInit(int elementCount) { if (elementCount < MaxArrayInitElementCountCache) { - if (_newArrayInit == null) - { - _newArrayInit = new Instruction[MaxArrayInitElementCountCache]; - } + _newArrayInit ??= new Instruction[MaxArrayInitElementCountCache]; return _newArrayInit[elementCount] ?? (_newArrayInit[elementCount] = new NewArrayInitInstruction(elementCount)); } diff --git a/src/System.Management.Automation/engine/interpreter/InstructionList.cs b/src/System.Management.Automation/engine/interpreter/InstructionList.cs index cbf831f3377..93946bfd2c3 100644 --- a/src/System.Management.Automation/engine/interpreter/InstructionList.cs +++ b/src/System.Management.Automation/engine/interpreter/InstructionList.cs @@ -233,10 +233,7 @@ private void UpdateStackDepth(Instruction instruction) public void SetDebugCookie(object cookie) { #if DEBUG - if (_debugCookies == null) - { - _debugCookies = new List>(); - } + _debugCookies ??= new List>(); Debug.Assert(Count > 0); _debugCookies.Add(new KeyValuePair(Count - 1, cookie)); @@ -372,10 +369,7 @@ public void EmitLoad(object value, Type type) int i = (int)value; if (i >= PushIntMinCachedValue && i <= PushIntMaxCachedValue) { - if (s_ints == null) - { - s_ints = new Instruction[PushIntMaxCachedValue - PushIntMinCachedValue + 1]; - } + s_ints ??= new Instruction[PushIntMaxCachedValue - PushIntMinCachedValue + 1]; i -= PushIntMinCachedValue; Emit(s_ints[i] ?? (s_ints[i] = new LoadObjectInstruction(value))); @@ -387,10 +381,7 @@ public void EmitLoad(object value, Type type) if (_objects == null) { _objects = new List(); - if (s_loadObjectCached == null) - { - s_loadObjectCached = new Instruction[CachedObjectCount]; - } + s_loadObjectCached ??= new Instruction[CachedObjectCount]; } if (_objects.Count < s_loadObjectCached.Length) @@ -451,10 +442,7 @@ internal void SwitchToBoxed(int index, int instructionIndex) public void EmitLoadLocal(int index) { - if (s_loadLocal == null) - { - s_loadLocal = new Instruction[LocalInstrCacheSize]; - } + s_loadLocal ??= new Instruction[LocalInstrCacheSize]; if (index < s_loadLocal.Length) { @@ -473,10 +461,7 @@ public void EmitLoadLocalBoxed(int index) internal static Instruction LoadLocalBoxed(int index) { - if (s_loadLocalBoxed == null) - { - s_loadLocalBoxed = new Instruction[LocalInstrCacheSize]; - } + s_loadLocalBoxed ??= new Instruction[LocalInstrCacheSize]; if (index < s_loadLocalBoxed.Length) { @@ -490,10 +475,7 @@ internal static Instruction LoadLocalBoxed(int index) public void EmitLoadLocalFromClosure(int index) { - if (s_loadLocalFromClosure == null) - { - s_loadLocalFromClosure = new Instruction[LocalInstrCacheSize]; - } + s_loadLocalFromClosure ??= new Instruction[LocalInstrCacheSize]; if (index < s_loadLocalFromClosure.Length) { @@ -507,10 +489,7 @@ public void EmitLoadLocalFromClosure(int index) public void EmitLoadLocalFromClosureBoxed(int index) { - if (s_loadLocalFromClosureBoxed == null) - { - s_loadLocalFromClosureBoxed = new Instruction[LocalInstrCacheSize]; - } + s_loadLocalFromClosureBoxed ??= new Instruction[LocalInstrCacheSize]; if (index < s_loadLocalFromClosureBoxed.Length) { @@ -524,10 +503,7 @@ public void EmitLoadLocalFromClosureBoxed(int index) public void EmitAssignLocal(int index) { - if (s_assignLocal == null) - { - s_assignLocal = new Instruction[LocalInstrCacheSize]; - } + s_assignLocal ??= new Instruction[LocalInstrCacheSize]; if (index < s_assignLocal.Length) { @@ -541,10 +517,7 @@ public void EmitAssignLocal(int index) public void EmitStoreLocal(int index) { - if (s_storeLocal == null) - { - s_storeLocal = new Instruction[LocalInstrCacheSize]; - } + s_storeLocal ??= new Instruction[LocalInstrCacheSize]; if (index < s_storeLocal.Length) { @@ -563,10 +536,7 @@ public void EmitAssignLocalBoxed(int index) internal static Instruction AssignLocalBoxed(int index) { - if (s_assignLocalBoxed == null) - { - s_assignLocalBoxed = new Instruction[LocalInstrCacheSize]; - } + s_assignLocalBoxed ??= new Instruction[LocalInstrCacheSize]; if (index < s_assignLocalBoxed.Length) { @@ -585,10 +555,7 @@ public void EmitStoreLocalBoxed(int index) internal static Instruction StoreLocalBoxed(int index) { - if (s_storeLocalBoxed == null) - { - s_storeLocalBoxed = new Instruction[LocalInstrCacheSize]; - } + s_storeLocalBoxed ??= new Instruction[LocalInstrCacheSize]; if (index < s_storeLocalBoxed.Length) { @@ -602,10 +569,7 @@ internal static Instruction StoreLocalBoxed(int index) public void EmitAssignLocalToClosure(int index) { - if (s_assignLocalToClosure == null) - { - s_assignLocalToClosure = new Instruction[LocalInstrCacheSize]; - } + s_assignLocalToClosure ??= new Instruction[LocalInstrCacheSize]; if (index < s_assignLocalToClosure.Length) { @@ -647,10 +611,7 @@ internal void EmitInitializeParameter(int index) internal static Instruction Parameter(int index) { - if (s_parameter == null) - { - s_parameter = new Instruction[LocalInstrCacheSize]; - } + s_parameter ??= new Instruction[LocalInstrCacheSize]; if (index < s_parameter.Length) { @@ -662,10 +623,7 @@ internal static Instruction Parameter(int index) internal static Instruction ParameterBox(int index) { - if (s_parameterBox == null) - { - s_parameterBox = new Instruction[LocalInstrCacheSize]; - } + s_parameterBox ??= new Instruction[LocalInstrCacheSize]; if (index < s_parameterBox.Length) { @@ -677,10 +635,7 @@ internal static Instruction ParameterBox(int index) internal static Instruction InitReference(int index) { - if (s_initReference == null) - { - s_initReference = new Instruction[LocalInstrCacheSize]; - } + s_initReference ??= new Instruction[LocalInstrCacheSize]; if (index < s_initReference.Length) { @@ -692,10 +647,7 @@ internal static Instruction InitReference(int index) internal static Instruction InitImmutableRefBox(int index) { - if (s_initImmutableRefBox == null) - { - s_initImmutableRefBox = new Instruction[LocalInstrCacheSize]; - } + s_initImmutableRefBox ??= new Instruction[LocalInstrCacheSize]; if (index < s_initImmutableRefBox.Length) { @@ -1127,10 +1079,7 @@ private RuntimeLabel[] BuildRuntimeLabels() public BranchLabel MakeLabel() { - if (_labels == null) - { - _labels = new List(); - } + _labels ??= new List(); var label = new BranchLabel(); _labels.Add(label); diff --git a/src/System.Management.Automation/engine/interpreter/LabelInfo.cs b/src/System.Management.Automation/engine/interpreter/LabelInfo.cs index 639be5204cc..80e5ca25157 100644 --- a/src/System.Management.Automation/engine/interpreter/LabelInfo.cs +++ b/src/System.Management.Automation/engine/interpreter/LabelInfo.cs @@ -176,10 +176,7 @@ internal void ValidateFinish() private void EnsureLabel(LightCompiler compiler) { - if (_label == null) - { - _label = compiler.Instructions.MakeLabel(); - } + _label ??= compiler.Instructions.MakeLabel(); } private bool DefinedIn(LabelScopeInfo scope) @@ -359,10 +356,7 @@ internal void AddLabelInfo(LabelTarget target, LabelInfo info) { Debug.Assert(CanJumpInto); - if (_labels == null) - { - _labels = new HybridReferenceDictionary(); - } + _labels ??= new HybridReferenceDictionary(); _labels[target] = info; } diff --git a/src/System.Management.Automation/engine/interpreter/LightCompiler.cs b/src/System.Management.Automation/engine/interpreter/LightCompiler.cs index 279a36baac7..e88606fc2fe 100644 --- a/src/System.Management.Automation/engine/interpreter/LightCompiler.cs +++ b/src/System.Management.Automation/engine/interpreter/LightCompiler.cs @@ -1132,10 +1132,7 @@ private void CompileLabelExpression(Expression expr) Debug.Assert(label != null); } - if (label == null) - { - label = DefineLabel(node.Target); - } + label ??= DefineLabel(node.Target); if (node.DefaultValue != null) { diff --git a/src/System.Management.Automation/engine/interpreter/LocalVariables.cs b/src/System.Management.Automation/engine/interpreter/LocalVariables.cs index d52f1fc6840..0c1f6416741 100644 --- a/src/System.Management.Automation/engine/interpreter/LocalVariables.cs +++ b/src/System.Management.Automation/engine/interpreter/LocalVariables.cs @@ -163,10 +163,7 @@ public LocalDefinition DefineLocal(ParameterExpression variable, int start) if (_variables.TryGetValue(variable, out existing)) { newScope = new VariableScope(result, start, existing); - if (existing.ChildScopes == null) - { - existing.ChildScopes = new List(); - } + existing.ChildScopes ??= new List(); existing.ChildScopes.Add(newScope); } @@ -296,10 +293,7 @@ internal Dictionary ClosureVariables internal LocalVariable AddClosureVariable(ParameterExpression variable) { - if (_closureVariables == null) - { - _closureVariables = new Dictionary(); - } + _closureVariables ??= new Dictionary(); LocalVariable result = new LocalVariable(_closureVariables.Count, true, false); _closureVariables.Add(variable, result); diff --git a/src/System.Management.Automation/engine/interpreter/LoopCompiler.cs b/src/System.Management.Automation/engine/interpreter/LoopCompiler.cs index 6ff0e36f5c3..78c5534a132 100644 --- a/src/System.Management.Automation/engine/interpreter/LoopCompiler.cs +++ b/src/System.Management.Automation/engine/interpreter/LoopCompiler.cs @@ -374,10 +374,7 @@ private Expression VisitVariable(ParameterExpression node, ExpressionAccess acce private ParameterExpression AddTemp(ParameterExpression variable) { - if (_temps == null) - { - _temps = new List(); - } + _temps ??= new List(); _temps.Add(variable); return variable; diff --git a/src/System.Management.Automation/engine/lang/scriptblock.cs b/src/System.Management.Automation/engine/lang/scriptblock.cs index 0c6c1d5dff1..451d015fdcc 100644 --- a/src/System.Management.Automation/engine/lang/scriptblock.cs +++ b/src/System.Management.Automation/engine/lang/scriptblock.cs @@ -545,7 +545,7 @@ internal T InvokeAsMemberFunctionT(object instance, object[] args) // is a pipeline that emits nothing then result.Count will // be zero so we catch that and "convert" it to null. Note that // the return statement is still required in the method, it - // just recieves nothing from it's argument. + // just receives nothing from it's argument. if (result.Count == 0) { return default(T); @@ -1035,10 +1035,7 @@ internal void InvokeWithPipe( processInCurrentThread: true, waitForCompletionInCurrentThread: true); - if (scriptBlockInvocationEventArgs.Exception != null) - { - scriptBlockInvocationEventArgs.Exception.Throw(); - } + scriptBlockInvocationEventArgs.Exception?.Throw(); } } @@ -1280,7 +1277,42 @@ public Array End() { // then pop this pipeline and dispose it... _context.PopPipelineProcessor(true); - _pipeline.Dispose(); + Dispose(); + } + } + + /// + /// Clean resources for script commands of this steppable pipeline. + /// + /// + /// The way we handle 'Clean' blocks in a steppable pipeline makes sure that: + /// 1. The 'Clean' blocks get to run if any exception is thrown from 'Begin/Process/End'. + /// 2. The 'Clean' blocks get to run if 'End' finished successfully. + /// However, this is not enough for a steppable pipeline, because the function, where the steppable + /// pipeline gets used, may fail (think about a proxy function). And that may lead to the situation + /// where "no exception was thrown from the steppable pipeline" but "the steppable pipeline didn't + /// run to the end". In that case, 'Clean' won't run unless it's triggered explicitly on the steppable + /// pipeline. This method allows a user to do that from the 'Clean' block of the proxy function. + /// + public void Clean() + { + if (_pipeline.Commands is null) + { + // The pipeline commands have been disposed. In this case, 'Clean' + // should have already been called on the pipeline processor. + return; + } + + try + { + _context.PushPipelineProcessor(_pipeline); + _pipeline.DoCleanup(); + } + finally + { + // then pop this pipeline and dispose it... + _context.PopPipelineProcessor(true); + Dispose(); } } @@ -1293,23 +1325,13 @@ public Array End() /// When this object is disposed, the contained pipeline should also be disposed. /// public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) { if (_disposed) { return; } - if (disposing) - { - _pipeline.Dispose(); - } - + _pipeline.Dispose(); _disposed = true; } diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index bcddbee0b24..cdf053dcb2e 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -252,6 +252,9 @@ internal static class CachedReflectionInfo new Type[] { typeof(int), typeof(IEqualityComparer) }, null); + internal static readonly MethodInfo ByRefOps_GetByRefPropertyValue = + typeof(ByRefOps).GetMethod(nameof(ByRefOps.GetByRefPropertyValue), StaticFlags); + internal static readonly MethodInfo HashtableOps_Add = typeof(HashtableOps).GetMethod(nameof(HashtableOps.Add), StaticFlags); @@ -639,7 +642,9 @@ internal static class CachedReflectionInfo internal static readonly MethodInfo ArgumentTransformationAttribute_Transform = typeof(ArgumentTransformationAttribute).GetMethod(nameof(ArgumentTransformationAttribute.Transform), InstancePublicFlags); - // ReSharper restore InconsistentNaming + + internal static readonly MethodInfo MemberInvocationLoggingOps_LogMemberInvocation = + typeof(MemberInvocationLoggingOps).GetMethod(nameof(MemberInvocationLoggingOps.LogMemberInvocation), StaticFlags); } internal static class ExpressionCache @@ -1059,7 +1064,7 @@ internal static Expression CallSetVariable(Expression variablePath, Expression r internal Expression GetAutomaticVariable(VariableExpressionAst varAst) { - // Generate, in psuedo code: + // Generate, in pseudo code: // // return (localsTuple.IsValueSet(tupleIndex) // ? localsTuple.ItemXXX @@ -1069,7 +1074,7 @@ internal Expression GetAutomaticVariable(VariableExpressionAst varAst) // // * $PSCmdlet - always set if the script uses cmdletbinding. // * $_ - always set in process and end block, otherwise need dynamic checks. - // * $this - can never know if it's set, always need above psuedo code. + // * $this - can never know if it's set, always need above pseudo code. // * $input - also can never know - it's always set from a command process, but not necessarily set from ScriptBlock.Invoke. // // These optimizations are not yet performed. @@ -1105,10 +1110,7 @@ internal static Expression CallStringEquals(Expression left, Expression right, b internal static Expression IsStrictMode(int version, Expression executionContext = null) { - if (executionContext == null) - { - executionContext = ExpressionCache.NullExecutionContext; - } + executionContext ??= ExpressionCache.NullExecutionContext; return Expression.Call( CachedReflectionInfo.ExecutionContext_IsStrictVersion, @@ -1174,7 +1176,7 @@ internal static Type GetTypeConstraintForMethodResolution(ExpressionAst expr) internal static PSMethodInvocationConstraints CombineTypeConstraintForMethodResolution(Type targetType, Type argType) { - if (targetType == null && argType == null) + if (targetType is null && argType is null) { return null; } @@ -1182,14 +1184,19 @@ internal static PSMethodInvocationConstraints CombineTypeConstraintForMethodReso return new PSMethodInvocationConstraints(targetType, new[] { argType }); } - internal static PSMethodInvocationConstraints CombineTypeConstraintForMethodResolution(Type targetType, Type[] argTypes) + internal static PSMethodInvocationConstraints CombineTypeConstraintForMethodResolution( + Type targetType, + Type[] argTypes, + object[] genericArguments = null) { - if (targetType == null && (argTypes == null || argTypes.Length == 0)) + if (targetType is null + && (argTypes is null || argTypes.Length == 0) + && (genericArguments is null || genericArguments.Length == 0)) { return null; } - return new PSMethodInvocationConstraints(targetType, argTypes); + return new PSMethodInvocationConstraints(targetType, argTypes, genericArguments); } internal static Expression ConvertValue(TypeConstraintAst typeConstraint, Expression expr) @@ -1748,18 +1755,15 @@ internal static Attribute GetAttribute(AttributeAst attributeAst) // Unwrap the wrapped exception var innerException = tie.InnerException; var rte = innerException as RuntimeException; - if (rte == null) - { - rte = InterpreterError.NewInterpreterExceptionWithInnerException( - null, - typeof(RuntimeException), - attributeAst.Extent, - "ExceptionConstructingAttribute", - ExtendedTypeSystem.ExceptionConstructingAttribute, - innerException, - innerException.Message, - attributeAst.TypeName.FullName); - } + rte ??= InterpreterError.NewInterpreterExceptionWithInnerException( + null, + typeof(RuntimeException), + attributeAst.Extent, + "ExceptionConstructingAttribute", + ExtendedTypeSystem.ExceptionConstructingAttribute, + innerException, + innerException.Message, + attributeAst.TypeName.FullName); InterpreterError.UpdateExceptionErrorRecordPosition(rte, attributeAst.Extent); throw rte; @@ -2026,6 +2030,7 @@ internal void Compile(CompiledScriptBlockData scriptBlock, bool optimize) scriptBlock.BeginBlock = CompileTree(_beginBlockLambda, compileInterpretChoice); scriptBlock.ProcessBlock = CompileTree(_processBlockLambda, compileInterpretChoice); scriptBlock.EndBlock = CompileTree(_endBlockLambda, compileInterpretChoice); + scriptBlock.CleanBlock = CompileTree(_cleanBlockLambda, compileInterpretChoice); scriptBlock.LocalsMutableTupleType = LocalVariablesTupleType; scriptBlock.LocalsMutableTupleCreator = MutableTuple.TupleCreator(LocalVariablesTupleType); scriptBlock.NameToIndexMap = nameToIndexMap; @@ -2036,16 +2041,14 @@ internal void Compile(CompiledScriptBlockData scriptBlock, bool optimize) scriptBlock.UnoptimizedBeginBlock = CompileTree(_beginBlockLambda, compileInterpretChoice); scriptBlock.UnoptimizedProcessBlock = CompileTree(_processBlockLambda, compileInterpretChoice); scriptBlock.UnoptimizedEndBlock = CompileTree(_endBlockLambda, compileInterpretChoice); + scriptBlock.UnoptimizedCleanBlock = CompileTree(_cleanBlockLambda, compileInterpretChoice); scriptBlock.UnoptimizedLocalsMutableTupleType = LocalVariablesTupleType; scriptBlock.UnoptimizedLocalsMutableTupleCreator = MutableTuple.TupleCreator(LocalVariablesTupleType); } // The sequence points are identical optimized or not. Regardless, we want to ensure // that the list is unique no matter when the property is accessed, so make sure it is set just once. - if (scriptBlock.SequencePoints == null) - { - scriptBlock.SequencePoints = _sequencePoints.ToArray(); - } + scriptBlock.SequencePoints ??= _sequencePoints.ToArray(); } private static Action CompileTree(Expression> lambda, CompileInterpretChoice compileInterpretChoice) @@ -2114,10 +2117,7 @@ private static object GetExpressionValue( // Can't be exposed to untrusted input - invoking arbitrary code could result in remote code // execution. - if (lambda == null) - { - lambda = (new Compiler()).CompileSingleExpression(expressionAst, out sequencePoints, out localsTupleType); - } + lambda ??= (new Compiler()).CompileSingleExpression(expressionAst, out sequencePoints, out localsTupleType); SessionStateInternal oldSessionState = context.EngineSessionState; try @@ -2221,6 +2221,7 @@ internal LoopGotoTargets(string label, LabelTarget breakLabel, LabelTarget conti private Expression> _beginBlockLambda; private Expression> _processBlockLambda; private Expression> _endBlockLambda; + private Expression> _cleanBlockLambda; private readonly List _loopTargets = new List(); private bool _generatingWhileOrDoLoop; @@ -2463,6 +2464,13 @@ public object VisitScriptBlock(ScriptBlockAst scriptBlockAst) } _endBlockLambda = CompileNamedBlock(scriptBlockAst.EndBlock, funcName, rootForDefiningTypesAndUsings); + rootForDefiningTypesAndUsings = null; + } + + if (scriptBlockAst.CleanBlock != null) + { + _cleanBlockLambda = CompileNamedBlock(scriptBlockAst.CleanBlock, funcName + "", rootForDefiningTypesAndUsings); + rootForDefiningTypesAndUsings = null; } return null; @@ -3887,7 +3895,7 @@ private Expression GetRedirectedExpression(CommandExpressionAst commandExpr, boo // funcContext.OutputPipe = oldPipe; // } // - // In the above psuedo-code, any of {outputFileRedirection, nonOutputFileRedirection, mergingRedirection} may + // In the above pseudo-code, any of {outputFileRedirection, nonOutputFileRedirection, mergingRedirection} may // not exist, but the order is preserved, so that file redirections go before merging redirections (so that // funcContext.OutputPipe has the correct value when setting up merging.) // @@ -3926,10 +3934,7 @@ private Expression GetRedirectedExpression(CommandExpressionAst commandExpr, boo // This will simply return a Linq.Expression representing the redirection. var compiledRedirection = VisitFileRedirection(fileRedirectionAst); - if (extraFileRedirectExprs == null) - { - extraFileRedirectExprs = new List(commandExpr.Redirections.Count); - } + extraFileRedirectExprs ??= new List(commandExpr.Redirections.Count); // Hold the current 'FileRedirection' instance for later use var redirectionExpr = NewTemp(typeof(FileRedirection), "fileRedirection"); @@ -5158,7 +5163,7 @@ public object VisitCatchClause(CatchClauseAst catchClauseAst) // If the automatic var has no value in the current frame, then we set the variable's value to $null // after leaving the stmt. // - // The psuedo-code: + // The pseudo-code: // // try { // oldValue = (localSet.Get(automaticVar)) ? locals.ItemNNN : null; @@ -5597,7 +5602,9 @@ public object VisitReturnStatement(ReturnStatementAst returnStatementAst) return Expression.Block(returnValue, returnExpr); } - return returnExpr; + return Expression.Block( + UpdatePosition(returnStatementAst), + returnExpr); } public object VisitExitStatement(ExitStatementAst exitStatementAst) @@ -6113,9 +6120,7 @@ public object VisitConvertExpression(ConvertExpressionAst convertExpressionAst) { // We'll wrap the variable in a PSReference, but not the constant variables ($true, $false, $null) because those // can't be changed. - IEnumerable unused1; - bool unused2; - var varType = varExpr.GetVariableType(this, out unused1, out unused2); + var varType = varExpr.GetVariableType(this, out _, out _); return Expression.Call( CachedReflectionInfo.VariableOps_GetVariableAsRef, Expression.Constant(varExpr.VariablePath), @@ -6126,10 +6131,7 @@ public object VisitConvertExpression(ConvertExpressionAst convertExpressionAst) } } - if (childExpr == null) - { - childExpr = Compile(convertExpressionAst.Child); - } + childExpr ??= Compile(convertExpressionAst.Child); if (typeName.FullName.Equals("PSCustomObject", StringComparison.OrdinalIgnoreCase)) { @@ -6326,17 +6328,48 @@ public object VisitMemberExpression(MemberExpressionAst memberExpressionAst) internal static PSMethodInvocationConstraints GetInvokeMemberConstraints(InvokeMemberExpressionAst invokeMemberExpressionAst) { - var arguments = invokeMemberExpressionAst.Arguments; + ReadOnlyCollection arguments = invokeMemberExpressionAst.Arguments; + Type[] argumentTypes = null; + if (arguments is not null) + { + argumentTypes = new Type[arguments.Count]; + for (var i = 0; i < arguments.Count; i++) + { + argumentTypes[i] = GetTypeConstraintForMethodResolution(arguments[i]); + } + } + var targetTypeConstraint = GetTypeConstraintForMethodResolution(invokeMemberExpressionAst.Expression); - return CombineTypeConstraintForMethodResolution( - targetTypeConstraint, - arguments?.Select(Compiler.GetTypeConstraintForMethodResolution).ToArray()); + + ReadOnlyCollection genericArguments = invokeMemberExpressionAst.GenericTypeArguments; + object[] genericTypeArguments = null; + if (genericArguments is not null) + { + genericTypeArguments = new object[genericArguments.Count]; + for (var i = 0; i < genericArguments.Count; i++) + { + Type type = genericArguments[i].GetReflectionType(); + genericTypeArguments[i] = (object)type ?? genericArguments[i]; + } + } + + return CombineTypeConstraintForMethodResolution(targetTypeConstraint, argumentTypes, genericTypeArguments); } internal static PSMethodInvocationConstraints GetInvokeMemberConstraints(BaseCtorInvokeMemberExpressionAst invokeMemberExpressionAst) { Type targetTypeConstraint = null; - var arguments = invokeMemberExpressionAst.Arguments; + ReadOnlyCollection arguments = invokeMemberExpressionAst.Arguments; + Type[] argumentTypes = null; + if (arguments is not null) + { + argumentTypes = new Type[arguments.Count]; + for (var i = 0; i < arguments.Count; i++) + { + argumentTypes[i] = GetTypeConstraintForMethodResolution(arguments[i]); + } + } + TypeDefinitionAst typeDefinitionAst = Ast.GetAncestorTypeDefinitionAst(invokeMemberExpressionAst); if (typeDefinitionAst != null) { @@ -6347,9 +6380,7 @@ internal static PSMethodInvocationConstraints GetInvokeMemberConstraints(BaseCto Diagnostics.Assert(false, "BaseCtorInvokeMemberExpressionAst must be used only inside TypeDefinitionAst"); } - return CombineTypeConstraintForMethodResolution( - targetTypeConstraint, - arguments?.Select(Compiler.GetTypeConstraintForMethodResolution).ToArray()); + return CombineTypeConstraintForMethodResolution(targetTypeConstraint, argumentTypes, genericArguments: null); } internal Expression InvokeMember( @@ -6934,10 +6965,7 @@ public void AddInstructions(LightCompiler compiler) compiler.PopLabelBlock(LabelScopeKind.Statement); // If enterLoop is null, we will never JIT compile the loop. - if (enterLoop != null) - { - enterLoop.FinishLoop(compiler.Instructions.Count); - } + enterLoop?.FinishLoop(compiler.Instructions.Count); } } diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs index 3afc55e9861..31e7618a32f 100644 --- a/src/System.Management.Automation/engine/parser/Parser.cs +++ b/src/System.Management.Automation/engine/parser/Parser.cs @@ -280,8 +280,7 @@ internal static ITypeName ScanType(string typename, bool ignoreErrors) var parser = new Parser(); var tokenizer = parser._tokenizer; tokenizer.Initialize(null, typename, null); - Token unused; - var result = parser.TypeNameRule(allowAssemblyQualifiedNames: true, firstTypeNameToken: out unused); + var result = parser.TypeNameRule(allowAssemblyQualifiedNames: true, firstTypeNameToken: out _); SemanticChecks.CheckArrayTypeNameDepth(result, PositionUtilities.EmptyExtent, parser); if (!ignoreErrors && parser.ErrorList.Count > 0) @@ -444,8 +443,7 @@ private Token NextToken() private Token PeekToken() { Token token = _ungotToken ?? _tokenizer.NextToken(); - if (_ungotToken == null) - _ungotToken = token; + _ungotToken ??= token; return token; } @@ -724,12 +722,13 @@ internal static bool TryParseAsConstantHashtable(string input, out Hashtable res ParseError[] parseErrors; var ast = Parser.ParseInput(input, out throwAwayTokens, out parseErrors); - if ((ast == null) || - parseErrors.Length > 0 || - ast.BeginBlock != null || - ast.ProcessBlock != null || - ast.DynamicParamBlock != null || - ast.EndBlock.Traps != null) + if (ast == null + || parseErrors.Length > 0 + || ast.BeginBlock != null + || ast.ProcessBlock != null + || ast.CleanBlock != null + || ast.DynamicParamBlock != null + || ast.EndBlock.Traps != null) { return false; } @@ -823,10 +822,7 @@ private List UsingStatementsRule() SkipToken(); var statement = UsingStatementRule(token); SkipNewlinesAndSemicolons(); - if (result == null) - { - result = new List(); - } + result ??= new List(); var usingStatement = statement as UsingStatementAst; // otherwise returned statement is ErrorStatementAst. @@ -1357,7 +1353,7 @@ private ITypeName FinishTypeNameRule(Token typeName, bool unBracketedGenericArg case TokenKind.LBracket: case TokenKind.Identifier: - return GenericTypeArgumentsRule(typeName, token, unBracketedGenericArg); + return GenericTypeNameRule(typeName, token, unBracketedGenericArg); default: // ErrorRecovery: sync to ']', and return non-null to avoid cascading errors. @@ -1437,7 +1433,7 @@ private ITypeName GetSingleGenericArgument(Token firstToken) return typeName; } - private ITypeName GenericTypeArgumentsRule(Token genericTypeName, Token firstToken, bool unBracketedGenericArg) + private List GenericTypeArgumentsRule(Token firstToken, out Token lastToken) { Diagnostics.Assert(firstToken.Kind == TokenKind.Identifier || firstToken.Kind == TokenKind.LBracket, "unexpected first token"); RuntimeHelpers.EnsureSufficientExecutionStack(); @@ -1446,20 +1442,18 @@ private ITypeName GenericTypeArgumentsRule(Token genericTypeName, Token firstTok ITypeName typeName = GetSingleGenericArgument(firstToken); genericArguments.Add(typeName); - Token commaOrRBracketToken; - Token token; while (true) { SkipNewlines(); - commaOrRBracketToken = NextToken(); - if (commaOrRBracketToken.Kind != TokenKind.Comma) + lastToken = NextToken(); + if (lastToken.Kind != TokenKind.Comma) { break; } SkipNewlines(); - token = PeekToken(); + Token token = PeekToken(); if (token.Kind == TokenKind.Identifier || token.Kind == TokenKind.LBracket) { SkipToken(); @@ -1467,43 +1461,55 @@ private ITypeName GenericTypeArgumentsRule(Token genericTypeName, Token firstTok } else { - ReportIncompleteInput(After(commaOrRBracketToken), + ReportIncompleteInput( + After(lastToken), nameof(ParserStrings.MissingTypename), ParserStrings.MissingTypename); - typeName = new TypeName(commaOrRBracketToken.Extent, ":ErrorTypeName:"); + typeName = new TypeName(lastToken.Extent, ":ErrorTypeName:"); } genericArguments.Add(typeName); } - if (commaOrRBracketToken.Kind != TokenKind.RBracket) + return genericArguments; + } + + private ITypeName GenericTypeNameRule(Token genericTypeName, Token firstToken, bool unbracketedGenericArg) + { + List genericArguments = GenericTypeArgumentsRule(firstToken, out Token rBracketToken); + + if (rBracketToken.Kind != TokenKind.RBracket) { // ErrorRecovery: pretend we had the closing bracket and just continue on. - - UngetToken(commaOrRBracketToken); - ReportIncompleteInput(Before(commaOrRBracketToken), + UngetToken(rBracketToken); + ReportIncompleteInput( + Before(rBracketToken), nameof(ParserStrings.EndSquareBracketExpectedAtEndOfAttribute), ParserStrings.EndSquareBracketExpectedAtEndOfAttribute); - commaOrRBracketToken = null; + rBracketToken = null; } var openGenericType = new TypeName(genericTypeName.Extent, genericTypeName.Text); - var result = new GenericTypeName(ExtentOf(genericTypeName.Extent, ExtentFromFirstOf(commaOrRBracketToken, genericArguments.LastOrDefault(), firstToken)), - openGenericType, genericArguments); - token = PeekToken(); + var result = new GenericTypeName( + ExtentOf(genericTypeName.Extent, ExtentFromFirstOf(rBracketToken, genericArguments.LastOrDefault(), firstToken)), + openGenericType, + genericArguments); + + Token token = PeekToken(); if (token.Kind == TokenKind.LBracket) { SkipToken(); return CompleteArrayTypeName(result, openGenericType, NextToken()); } - if (token.Kind == TokenKind.Comma && !unBracketedGenericArg) + if (token.Kind == TokenKind.Comma && !unbracketedGenericArg) { SkipToken(); string assemblyNameSpec = _tokenizer.GetAssemblyNameSpec(); if (string.IsNullOrEmpty(assemblyNameSpec)) { - ReportError(After(token), + ReportError( + After(token), nameof(ParserStrings.MissingAssemblyNameSpecification), ParserStrings.MissingAssemblyNameSpecification); } @@ -1534,6 +1540,25 @@ private ITypeName CompleteArrayTypeName(ITypeName elementType, TypeName typeForA token = NextToken(); } while (token.Kind == TokenKind.Comma); + // The dimensions for an array must be less than or equal to 32. + // Search the doc for 'Type.MakeArrayType(int rank)' for more details. + if (dim > 32) + { + // If the next token is right bracket, we swallow it to make it easier to parse the rest of script. + // Otherwise, we unget the token for the subsequent parsing to consume. + if (token.Kind != TokenKind.RBracket) + { + UngetToken(token); + } + + ReportError( + ExtentOf(firstTokenAfterLBracket, lastComma), + nameof(ParserStrings.ArrayHasTooManyDimensions), + ParserStrings.ArrayHasTooManyDimensions, + arg: dim); + break; + } + if (token.Kind != TokenKind.RBracket) { // ErrorRecovery: just pretend we saw a ']'. @@ -1713,9 +1738,9 @@ private ScriptBlockAst NamedBlockListRule(Token lCurly, List NamedBlockAst beginBlock = null; NamedBlockAst processBlock = null; NamedBlockAst endBlock = null; - IScriptExtent startExtent = lCurly != null - ? lCurly.Extent - : paramBlockAst?.Extent; + NamedBlockAst cleanBlock = null; + + IScriptExtent startExtent = lCurly?.Extent ?? paramBlockAst?.Extent; IScriptExtent endExtent = null; IScriptExtent extent = null; IScriptExtent scriptBlockExtent = null; @@ -1757,13 +1782,11 @@ private ScriptBlockAst NamedBlockListRule(Token lCurly, List case TokenKind.Begin: case TokenKind.Process: case TokenKind.End: + case TokenKind.Clean: break; } - if (startExtent == null) - { - startExtent = blockNameToken.Extent; - } + startExtent ??= blockNameToken.Extent; endExtent = blockNameToken.Extent; @@ -1797,6 +1820,10 @@ private ScriptBlockAst NamedBlockListRule(Token lCurly, List { endBlock = new NamedBlockAst(extent, TokenKind.End, statementBlock, false); } + else if (blockNameToken.Kind == TokenKind.Clean && cleanBlock == null) + { + cleanBlock = new NamedBlockAst(extent, TokenKind.Clean, statementBlock, false); + } else if (blockNameToken.Kind == TokenKind.Dynamicparam && dynamicParamBlock == null) { dynamicParamBlock = new NamedBlockAst(extent, TokenKind.Dynamicparam, statementBlock, false); @@ -1818,7 +1845,14 @@ private ScriptBlockAst NamedBlockListRule(Token lCurly, List CompleteScriptBlockBody(lCurly, ref extent, out scriptBlockExtent); return_script_block_ast: - return new ScriptBlockAst(scriptBlockExtent, usingStatements, paramBlockAst, beginBlock, processBlock, endBlock, + return new ScriptBlockAst( + scriptBlockExtent, + usingStatements, + paramBlockAst, + beginBlock, + processBlock, + endBlock, + cleanBlock, dynamicParamBlock); } @@ -1893,10 +1927,7 @@ private IScriptExtent StatementListRule(List statements, List custom return null; } - if (configurationNameToken.Kind == TokenKind.EndOfInput) + if (configurationNameToken.Kind is TokenKind.EndOfInput or TokenKind.Comma) { UngetToken(configurationNameToken); @@ -3049,10 +3080,7 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom } finally { - if (p != null) - { - p.Dispose(); - } + p?.Dispose(); // // Put the parser back... @@ -3190,10 +3218,7 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom if (topLevel) { - if (_configurationKeywordsDefinedInThisFile == null) - { - _configurationKeywordsDefinedInThisFile = new Dictionary(); - } + _configurationKeywordsDefinedInThisFile ??= new Dictionary(); _configurationKeywordsDefinedInThisFile[keywordToAddForThisConfigurationStatement.Keyword] = keywordToAddForThisConfigurationStatement; } @@ -3548,10 +3573,7 @@ private StatementAst ForStatementRule(LabelToken labelToken, Token forToken) // ErrorRecovery: don't continue parsing the for statement. UngetToken(rParen); - if (endErrorStatement == null) - { - endErrorStatement = lParen.Extent; - } + endErrorStatement ??= lParen.Extent; ReportIncompleteInput(After(endErrorStatement), nameof(ParserStrings.MissingEndParenthesisAfterStatement), @@ -3860,10 +3882,7 @@ private StatementAst DynamicKeywordStatementRule(Token functionName, DynamicKeyw // we aren't expecting a name, we still do this so that the signature of the implementing function remains // the same. ExpressionAst originalInstanceName = instanceName; - if (instanceName == null) - { - instanceName = new StringConstantExpressionAst(nameToken.Extent, elementName, StringConstantType.BareWord); - } + instanceName ??= new StringConstantExpressionAst(nameToken.Extent, elementName, StringConstantType.BareWord); SkipNewlines(); @@ -4223,11 +4242,10 @@ private StatementAst ClassDefinitionRule(List customAttributes this.SkipToken(); SkipNewlines(); ITypeName superClass; - Token unused; Token commaToken = null; while (true) { - superClass = this.TypeNameRule(allowAssemblyQualifiedNames: false, firstTypeNameToken: out unused); + superClass = this.TypeNameRule(allowAssemblyQualifiedNames: false, firstTypeNameToken: out _); if (superClass == null) { ReportIncompleteInput(After(ExtentFromFirstOf(commaToken, colonToken)), @@ -4281,10 +4299,7 @@ private StatementAst ClassDefinitionRule(List customAttributes if (astsOnError != null && astsOnError.Count > 0) { - if (nestedAsts == null) - { - nestedAsts = new List(); - } + nestedAsts ??= new List(); nestedAsts.AddRange(astsOnError); lastExtent = astsOnError.Last().Extent; @@ -4313,10 +4328,7 @@ private StatementAst ClassDefinitionRule(List customAttributes var classDefn = new TypeDefinitionAst(extent, name.Value, customAttributes?.OfType(), members, TypeAttributes.Class, superClassesList); if (customAttributes != null && customAttributes.OfType().Any()) { - if (nestedAsts == null) - { - nestedAsts = new List(); - } + nestedAsts ??= new List(); // no need to report error since the error is reported in method StatementRule nestedAsts.AddRange(customAttributes.OfType()); nestedAsts.Add(classDefn); @@ -4380,10 +4392,7 @@ private MemberAst ClassMemberRule(string className, out List astsOnError) if (attribute != null) { lastAttribute = attribute; - if (startExtent == null) - { - startExtent = attribute.Extent; - } + startExtent ??= attribute.Extent; var attributeAst = attribute as AttributeAst; if (attributeAst != null) @@ -4403,10 +4412,7 @@ private MemberAst ClassMemberRule(string className, out List astsOnError) } token = PeekToken(); - if (startExtent == null) - { - startExtent = token.Extent; - } + startExtent ??= token.Extent; switch (token.Kind) { @@ -4621,10 +4627,7 @@ private static void RecordErrorAsts(Ast errAst, ref List astsOnError) return; } - if (astsOnError == null) - { - astsOnError = new List(); - } + astsOnError ??= new List(); astsOnError.Add(errAst); } @@ -4636,10 +4639,7 @@ private static void RecordErrorAsts(IEnumerable errAsts, ref List asts return; } - if (astsOnError == null) - { - astsOnError = new List(); - } + astsOnError ??= new List(); astsOnError.AddRange(errAsts); } @@ -4708,8 +4708,7 @@ private StatementAst EnumDefinitionRule(List customAttributes, this.SkipToken(); SkipNewlines(); ITypeName underlyingType; - Token unused; - underlyingType = this.TypeNameRule(allowAssemblyQualifiedNames: false, firstTypeNameToken: out unused); + underlyingType = this.TypeNameRule(allowAssemblyQualifiedNames: false, firstTypeNameToken: out _); if (underlyingType == null) { ReportIncompleteInput( @@ -4978,7 +4977,7 @@ private StatementAst UsingStatementRule(Token usingToken) SkipToken(); var aliasToken = NextToken(); - if (aliasToken.Kind == TokenKind.EndOfInput) + if (aliasToken.Kind is TokenKind.EndOfInput or TokenKind.NewLine or TokenKind.Semi) { UngetToken(aliasToken); ReportIncompleteInput(After(equalsToken), @@ -4987,6 +4986,12 @@ private StatementAst UsingStatementRule(Token usingToken) return new ErrorStatementAst(ExtentOf(usingToken, equalsToken)); } + if (aliasToken.Kind == TokenKind.Comma) + { + ReportError(aliasToken.Extent, nameof(ParserStrings.UnexpectedUnaryOperator), ParserStrings.UnexpectedUnaryOperator, aliasToken.Text); + return new ErrorStatementAst(ExtentOf(usingToken, aliasToken)); + } + var aliasAst = GetCommandArgument(CommandArgumentContext.CommandArgument, aliasToken); if (kind == UsingStatementKind.Module && aliasAst is HashtableAst) { @@ -4994,7 +4999,19 @@ private StatementAst UsingStatementRule(Token usingToken) } else if (aliasAst is not StringConstantExpressionAst) { - return new ErrorStatementAst(ExtentOf(usingToken, aliasAst), new Ast[] { itemAst, aliasAst }); + var errorExtent = ExtentFromFirstOf(aliasAst, aliasToken); + Ast[] nestedAsts; + if (aliasAst is null) + { + nestedAsts = new Ast[] { itemAst }; + } + else + { + nestedAsts = new Ast[] { itemAst, aliasAst }; + } + + ReportError(errorExtent, nameof(ParserStrings.InvalidValueForUsingItemName), ParserStrings.InvalidValueForUsingItemName, errorExtent.Text); + return new ErrorStatementAst(ExtentOf(usingToken, errorExtent), nestedAsts); } RequireStatementTerminator(); @@ -5184,7 +5201,7 @@ private StatementAst MethodDeclarationRule(Token functionNameToken, string class SkipToken(); // we don't allow syntax // : base{ script } - // as a short for for + // as a short for // : base( { script } ) baseCtorCallParams = InvokeParamParenListRule(lParen, out baseCallLastExtent); this.SkipNewlines(); @@ -5211,11 +5228,9 @@ private StatementAst MethodDeclarationRule(Token functionNameToken, string class SetTokenizerMode(oldTokenizerMode); } - if (baseCtorCallParams == null) - { + baseCtorCallParams ??= // Assuming implicit default ctor - baseCtorCallParams = new List(); - } + new List(); } Token lCurly = NextToken(); @@ -5504,10 +5519,7 @@ private CatchClauseAst CatchBlockRule(ref IScriptExtent endErrorStatement, ref L break; } - if (exceptionTypes == null) - { - exceptionTypes = new List(); - } + exceptionTypes ??= new List(); exceptionTypes.Add(typeConstraintAst); @@ -6010,10 +6022,7 @@ private PipelineBaseAst PipelineRule( { SkipToken(); - if (redirections == null) - { - redirections = new RedirectionAst[CommandBaseAst.MaxRedirections]; - } + redirections ??= new RedirectionAst[CommandBaseAst.MaxRedirections]; IScriptExtent unused = null; lastRedirection = RedirectionRule(redirectionToken, redirections, ref unused); @@ -6034,10 +6043,7 @@ private PipelineBaseAst PipelineRule( if (commandAst != null) { - if (startExtent == null) - { - startExtent = commandAst.Extent; - } + startExtent ??= commandAst.Extent; pipelineElements.Add(commandAst); } @@ -6390,10 +6396,7 @@ private ExpressionAst GetCommandArgument(CommandArgumentContext context, Token t } commaToken = token; - if (commandArgs == null) - { - commandArgs = new List(); - } + commandArgs ??= new List(); commandArgs.Add(exprAst); @@ -6561,10 +6564,7 @@ internal Ast CommandRule(bool forDynamicKeyword) case TokenKind.RedirectInStd: if ((context & CommandArgumentContext.CommandName) == 0) { - if (redirections == null) - { - redirections = new RedirectionAst[CommandBaseAst.MaxRedirections]; - } + redirections ??= new RedirectionAst[CommandBaseAst.MaxRedirections]; RedirectionRule((RedirectionToken)token, redirections, ref endExtent); } @@ -6703,7 +6703,7 @@ private ExpressionAst ExpressionRule(bool endNumberOnTernaryOpChars = false) SkipToken(); SkipNewlines(); - // We have seen the ternary operator '?' and now expecting the 'IfFalse' expression. + // We have seen the ternary operator '?' and now expecting the 'IfTrue' expression. ExpressionAst ifTrue = ExpressionRule(endNumberOnTernaryOpChars: true); if (ifTrue == null) { @@ -7166,10 +7166,7 @@ private ExpressionAst UnaryExpressionRule(bool endNumberOnTernaryOpChars = false } } - if (expr == null) - { - expr = new TypeExpressionAst(lastAttribute.Extent, lastAttribute.TypeName); - } + expr ??= new TypeExpressionAst(lastAttribute.Extent, lastAttribute.TypeName); } for (int i = attributes.Count - 2; i >= 0; --i) @@ -7715,34 +7712,128 @@ private ExpressionAst MemberAccessRule(ExpressionAst targetExpr, Token operatorT member = GetSingleCommandArgument(CommandArgumentContext.CommandArgument) ?? new ErrorExpressionAst(ExtentOf(targetExpr, operatorToken)); } - else + else if (_ungotToken == null) { + // Member name may be an incomplete token like `$a.$(Command-Name`, in which case, '_ungotToken != null'. + // We do not look for generic args or invocation token if the member name token is recognisably incomplete. + int resyncIndex = _tokenizer.GetRestorePoint(); + List genericTypeArguments = GenericMethodArgumentsRule(resyncIndex, out Token rBracket); Token lParen = NextInvokeMemberToken(); + if (lParen != null) { + // When we reach here, we either had a legit section of generic arguments (in which case, `rBracket` + // won't be null), or we saw `lParen` directly following the member token (in which case, `rBracket` + // will be null). + int endColumnNumber = rBracket is null ? member.Extent.EndColumnNumber : rBracket.Extent.EndColumnNumber; + Diagnostics.Assert(lParen.Kind == TokenKind.LParen || lParen.Kind == TokenKind.LCurly, "token kind incorrect"); - Diagnostics.Assert(member.Extent.EndColumnNumber == lParen.Extent.StartColumnNumber, - "member and paren must be adjacent"); - return MemberInvokeRule(targetExpr, lParen, operatorToken, member); + Diagnostics.Assert( + endColumnNumber == lParen.Extent.StartColumnNumber, + "member and paren must be adjacent when the method is not generic"); + return MemberInvokeRule(targetExpr, lParen, operatorToken, member, genericTypeArguments); + } + else if (rBracket != null) + { + // We had a legit section of generic arguments but no 'lParen' following that, so this is not a method + // invocation, but an invalid indexing operation. Resync the tokenizer back to before the generic arg + // parsing and then continue. + Resync(resyncIndex); } } return new MemberExpressionAst( - ExtentOf(targetExpr, member), - targetExpr, - member, - @static: operatorToken.Kind == TokenKind.ColonColon, - nullConditional: operatorToken.Kind == TokenKind.QuestionDot); + ExtentOf(targetExpr, member), + targetExpr, + member, + @static: operatorToken.Kind == TokenKind.ColonColon, + nullConditional: operatorToken.Kind == TokenKind.QuestionDot); } - private ExpressionAst MemberInvokeRule(ExpressionAst targetExpr, Token lBracket, Token operatorToken, CommandElementAst member) + private List GenericMethodArgumentsRule(int resyncIndex, out Token rBracketToken) + { + List genericTypes = null; + + Token lBracket = NextToken(); + rBracketToken = null; + + if (lBracket.Kind != TokenKind.LBracket) + { + // We cannot avoid this Resync(); if we use PeekToken() to try to avoid a Resync(), the method called + // after this [`NextInvokeMemberToken()` or `NextMemberAccessToken()`] will note that an _ungotToken + // is present and assume an error state. That will cause any property accesses or non-generic method + // calls to throw a parse error. + Resync(resyncIndex); + return null; + } + + // This is either a InvokeMember expression with generic type arguments, or some sort of collection index + // on a property. + TokenizerMode oldTokenizerMode = _tokenizer.Mode; + try + { + // Switch to typename mode to avoid aggressive argument tokenization. + SetTokenizerMode(TokenizerMode.TypeName); + + SkipNewlines(); + Token firstToken = NextToken(); + + // For method generic arguments, we only support the syntax `$var.Method[TypeName1 <, TypeName2 ...>]`, + // not the syntax `$var.Method[[TypeName1] <, [TypeName2] ...>]`. + // The latter syntax has been supported for type expression since the beginning, but it's ambiguous in + // this scenario because we could be looking at an indexing operation on a property like: + // `$var.Property[]` + // and the `` could start with a type expression like `[TypeName]::Method()`, or even just + // a single type expression acting as a key to a hashtable property. Such cases will cause ambiguities. + // + // It could be possible to write code that sorts out the ambiguity and continue to support the latter + // syntax for method generic arguments, and thus to allow assembly-qualified type names. But we choose + // not to do so because: + // 1. that will definitely increase the complexity of the parsing code and also make it fragile; + // 2. the latter syntax hurts readability a lot due to the number of opening/closing brackets. + // The downside is that the assembly-qualified type names won't be supported for method generic args, + // but that's likely not a problem in practice, and we can revisit if it turns out otherwise. + if (firstToken.Kind == TokenKind.Identifier) + { + resyncIndex = -1; + genericTypes = GenericTypeArgumentsRule(firstToken, out rBracketToken); + + if (rBracketToken.Kind != TokenKind.RBracket) + { + UngetToken(rBracketToken); + ReportIncompleteInput( + Before(rBracketToken), + nameof(ParserStrings.EndSquareBracketExpectedAtEndOfType), + ParserStrings.EndSquareBracketExpectedAtEndOfType); + rBracketToken = null; + } + } + } + finally + { + SetTokenizerMode(oldTokenizerMode); + + if (resyncIndex > 0) + { + Resync(resyncIndex); + } + } + + return genericTypes; + } + + private ExpressionAst MemberInvokeRule( + ExpressionAst targetExpr, + Token lBracket, + Token operatorToken, + CommandElementAst member, + IList genericTypes) { // G invocation-expression: target-expression passed as a parameter. lBracket can be '(' or '{'. // G target-expression member-name invoke-param-list // G invoke-param-list: // G '(' invoke-param-paren-list // G script-block - IScriptExtent lastExtent = null; List arguments; @@ -7753,6 +7844,7 @@ private ExpressionAst MemberInvokeRule(ExpressionAst targetExpr, Token lBracket, else { arguments = new List(); + // handle the construct $x.methodName{2+2} as through it had been written $x.methodName({2+2}) SkipNewlines(); ExpressionAst argument = ScriptBlockExpressionRule(lBracket); @@ -7766,7 +7858,8 @@ private ExpressionAst MemberInvokeRule(ExpressionAst targetExpr, Token lBracket, member, arguments, operatorToken.Kind == TokenKind.ColonColon, - operatorToken.Kind == TokenKind.QuestionDot); + operatorToken.Kind == TokenKind.QuestionDot, + genericTypes); } private List InvokeParamParenListRule(Token lParen, out IScriptExtent lastExtent) diff --git a/src/System.Management.Automation/engine/parser/PreOrderVisitor.cs b/src/System.Management.Automation/engine/parser/PreOrderVisitor.cs index 33b2fadec64..91c0c6247e9 100644 --- a/src/System.Management.Automation/engine/parser/PreOrderVisitor.cs +++ b/src/System.Management.Automation/engine/parser/PreOrderVisitor.cs @@ -37,10 +37,7 @@ public abstract class AstVisitor internal AstVisitAction CheckForPostAction(Ast ast, AstVisitAction action) { var postActionHandler = this as IAstPostVisitHandler; - if (postActionHandler != null) - { - postActionHandler.PostVisit(ast); - } + postActionHandler?.PostVisit(ast); return action; } diff --git a/src/System.Management.Automation/engine/parser/SafeValues.cs b/src/System.Management.Automation/engine/parser/SafeValues.cs index f76269a12d7..0e41f87a318 100644 --- a/src/System.Management.Automation/engine/parser/SafeValues.cs +++ b/src/System.Management.Automation/engine/parser/SafeValues.cs @@ -548,10 +548,7 @@ public object VisitExpandableStringExpression(ExpandableStringExpressionAst expa ofs = t_context.SessionState.PSVariable.GetValue("OFS") as string; } - if (ofs == null) - { - ofs = " "; - } + ofs ??= " "; for (int offset = 0; offset < safeValues.Length; offset++) { diff --git a/src/System.Management.Automation/engine/parser/SemanticChecks.cs b/src/System.Management.Automation/engine/parser/SemanticChecks.cs index 927b2c33ae8..2e2dcadd645 100644 --- a/src/System.Management.Automation/engine/parser/SemanticChecks.cs +++ b/src/System.Management.Automation/engine/parser/SemanticChecks.cs @@ -406,10 +406,11 @@ public override AstVisitAction VisitFunctionMember(FunctionMemberAst functionMem ParserStrings.ParamBlockNotAllowedInMethod); } - if (body.BeginBlock != null || - body.ProcessBlock != null || - body.DynamicParamBlock != null || - !body.EndBlock.Unnamed) + if (body.BeginBlock != null + || body.ProcessBlock != null + || body.CleanBlock != null + || body.DynamicParamBlock != null + || !body.EndBlock.Unnamed) { _parser.ReportError(Parser.ExtentFromFirstOf(body.DynamicParamBlock, body.BeginBlock, body.ProcessBlock, body.EndBlock), nameof(ParserStrings.NamedBlockNotAllowedInMethod), @@ -919,6 +920,13 @@ public override AstVisitAction VisitConvertExpression(ConvertExpressionAst conve ParserStrings.OrderedAttributeOnlyOnHashLiteralNode, convertExpressionAst.Type.TypeName.FullName); } + + // Currently, the type name '[ordered]' is handled specially in PowerShell. + // When used in a conversion expression, it's only allowed on a hashliteral node, and it's + // always interpreted as an initializer for a case-insensitive + // 'System.Collections.Specialized.OrderedDictionary' by the compiler. + // So, we can return early from here. + return AstVisitAction.Continue; } if (typeof(PSReference) == convertExpressionAst.Type.TypeName.GetReflectionType()) diff --git a/src/System.Management.Automation/engine/parser/SymbolResolver.cs b/src/System.Management.Automation/engine/parser/SymbolResolver.cs index b97496dcc0f..18f694bf727 100644 --- a/src/System.Management.Automation/engine/parser/SymbolResolver.cs +++ b/src/System.Management.Automation/engine/parser/SymbolResolver.cs @@ -111,11 +111,8 @@ internal void AddTypeFromUsingModule(Parser parser, TypeDefinitionAst typeDefini TypeLookupResult result; if (_typeTable.TryGetValue(typeDefinitionAst.Name, out result)) { - if (result.ExternalNamespaces != null) - { - // override external type by the type defined in the current namespace - result.ExternalNamespaces.Add(moduleInfo.Name); - } + // override external type by the type defined in the current namespace + result.ExternalNamespaces?.Add(moduleInfo.Name); } else { diff --git a/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs b/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs index d1e3c7155d7..25bd7f999a5 100644 --- a/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs +++ b/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs @@ -298,7 +298,7 @@ internal void AddMembersByInferredTypeDefinitionAst( else { var functionMember = (FunctionMemberAst)member; - add = functionMember.IsStatic == isStatic; + add = (functionMember.IsConstructor && isStatic) || (!functionMember.IsConstructor && functionMember.IsStatic == isStatic); foundConstructor |= functionMember.IsConstructor; } @@ -638,6 +638,13 @@ object ICustomAstVisitor.VisitMergingRedirection(MergingRedirectionAst mergingRe object ICustomAstVisitor.VisitBinaryExpression(BinaryExpressionAst binaryExpressionAst) { + // TODO: Handle other kinds of expressions on the right side. + if (binaryExpressionAst.Operator == TokenKind.As && binaryExpressionAst.Right is TypeExpressionAst typeExpression) + { + var type = typeExpression.TypeName.GetReflectionType(); + var psTypeName = type != null ? new PSTypeName(type) : new PSTypeName(typeExpression.TypeName.FullName); + return new[] { psTypeName }; + } return InferTypes(binaryExpressionAst.Left); } @@ -1494,8 +1501,16 @@ private IEnumerable InferTypesFrom(MemberExpressionAst memberExpress return Array.Empty(); } + bool isInvokeMemberExpressionAst = false; var res = new List(10); - bool isInvokeMemberExpressionAst = memberExpressionAst is InvokeMemberExpressionAst; + IList genericTypeArguments = null; + + if (memberExpressionAst is InvokeMemberExpressionAst invokeMemberExpression) + { + isInvokeMemberExpressionAst = true; + genericTypeArguments = invokeMemberExpression.GenericTypeArguments; + } + var maybeWantDefaultCtor = isStatic && isInvokeMemberExpressionAst && memberAsStringConst.Value.EqualsOrdinalIgnoreCase("new"); @@ -1512,7 +1527,7 @@ private IEnumerable InferTypesFrom(MemberExpressionAst memberExpress var members = _context.GetMembersByInferredType(type, isStatic, filter: null); - AddTypesOfMembers(type, memberNameList, members, ref maybeWantDefaultCtor, isInvokeMemberExpressionAst, res); + AddTypesOfMembers(type, memberNameList, members, ref maybeWantDefaultCtor, isInvokeMemberExpressionAst, genericTypeArguments, res); // We didn't find any constructors but they used [T]::new() syntax if (maybeWantDefaultCtor) @@ -1533,7 +1548,7 @@ private void GetTypesOfMembers( List inferredTypes) { var memberNamesToCheck = new List { memberName }; - AddTypesOfMembers(thisType, memberNamesToCheck, members, ref maybeWantDefaultCtor, isInvokeMemberExpressionAst, inferredTypes); + AddTypesOfMembers(thisType, memberNamesToCheck, members, ref maybeWantDefaultCtor, isInvokeMemberExpressionAst, genericTypeArguments: null, inferredTypes); } private void AddTypesOfMembers( @@ -1542,6 +1557,7 @@ private void AddTypesOfMembers( IList members, ref bool maybeWantDefaultCtor, bool isInvokeMemberExpressionAst, + IList genericTypeArguments, List result) { for (int i = 0; i < memberNamesToCheck.Count; i++) @@ -1549,7 +1565,7 @@ private void AddTypesOfMembers( string memberNameToCheck = memberNamesToCheck[i]; foreach (var member in members) { - if (TryGetTypeFromMember(currentType, member, memberNameToCheck, ref maybeWantDefaultCtor, isInvokeMemberExpressionAst, result, memberNamesToCheck)) + if (TryGetTypeFromMember(currentType, member, memberNameToCheck, ref maybeWantDefaultCtor, isInvokeMemberExpressionAst, genericTypeArguments, result, memberNamesToCheck)) { break; } @@ -1563,6 +1579,7 @@ private bool TryGetTypeFromMember( string memberName, ref bool maybeWantDefaultCtor, bool isInvokeMemberExpressionAst, + IList genericTypeArguments, List result, List memberNamesToCheck) { @@ -1588,7 +1605,7 @@ private bool TryGetTypeFromMember( if (methodCacheEntry[0].method.Name.Equals(memberName, StringComparison.OrdinalIgnoreCase)) { maybeWantDefaultCtor = false; - AddTypesFromMethodCacheEntry(methodCacheEntry, result, isInvokeMemberExpressionAst); + AddTypesFromMethodCacheEntry(methodCacheEntry, genericTypeArguments, result, isInvokeMemberExpressionAst); return true; } @@ -1637,7 +1654,7 @@ private bool TryGetTypeFromMember( case PSMethod m: if (m.adapterData is DotNetAdapter.MethodCacheEntry methodCacheEntry) { - AddTypesFromMethodCacheEntry(methodCacheEntry, result, isInvokeMemberExpressionAst); + AddTypesFromMethodCacheEntry(methodCacheEntry, genericTypeArguments, result, isInvokeMemberExpressionAst); return true; } @@ -1701,16 +1718,58 @@ private bool TryGetTypeFromMember( private static void AddTypesFromMethodCacheEntry( DotNetAdapter.MethodCacheEntry methodCacheEntry, + IList genericTypeArguments, List result, bool isInvokeMemberExpressionAst) { if (isInvokeMemberExpressionAst) { + Type[] resolvedTypeArguments = null; + if (genericTypeArguments is not null) + { + resolvedTypeArguments = new Type[genericTypeArguments.Count]; + for (int i = 0; i < genericTypeArguments.Count; i++) + { + Type resolvedType = genericTypeArguments[i].GetReflectionType(); + if (resolvedType is null) + { + // If any generic type argument cannot be resolved yet, + // we simply assume this information is unavailable. + resolvedTypeArguments = null; + break; + } + + resolvedTypeArguments[i] = resolvedType; + } + } + + var tempResult = new HashSet(StringComparer.OrdinalIgnoreCase) { "System.Void" }; foreach (var method in methodCacheEntry.methodInformationStructures) { - if (method.method is MethodInfo methodInfo && !methodInfo.ReturnType.ContainsGenericParameters) + if (method.method is MethodInfo methodInfo) { - result.Add(new PSTypeName(methodInfo.ReturnType)); + Type retType = null; + if (!methodInfo.ReturnType.ContainsGenericParameters) + { + retType = methodInfo.ReturnType; + } + else if (resolvedTypeArguments is not null) + { + try + { + retType = methodInfo.MakeGenericMethod(resolvedTypeArguments).ReturnType; + } + catch + { + // If we can't build the generic method then just skip it to retain other completion results. + continue; + } + } + + if (retType is not null && tempResult.Add(retType.FullName)) + { + result.Add(new PSTypeName(retType)); + } } } @@ -1781,106 +1840,101 @@ private void InferTypeFrom(VariableExpressionAst variableExpressionAst, List 0) { - parent = parent.Parent.Parent.Parent.Parent; + parent = switchErrorStatement.Conditions[0]; } + break; } - - if (parent.Parent is CommandParameterAst) + else if (parent is ScriptBlockExpressionAst) { - parent = parent.Parent; + hasSeenScriptBlock = true; } - - if (parent is CatchClauseAst catchBlock) + else if (hasSeenScriptBlock) { - if (catchBlock.CatchTypes.Count > 0) + if (parent is InvokeMemberExpressionAst invokeMember) { - foreach (TypeConstraintAst catchType in catchBlock.CatchTypes) - { - Type exceptionType = catchType.TypeName.GetReflectionType(); - if (exceptionType != null && typeof(Exception).IsAssignableFrom(exceptionType)) - { - inferredTypes.Add(new PSTypeName(typeof(ErrorRecord<>).MakeGenericType(exceptionType))); - } - } + parent = invokeMember.Expression; + break; } - else + else if (parent is CommandAst cmdAst && cmdAst.Parent is PipelineAst pipeline && pipeline.PipelineElements.Count > 1) { - inferredTypes.Add(new PSTypeName(typeof(ErrorRecord))); + // We've found a pipeline with multiple commands, now we need to determine what command came before the command with the scriptblock: + // eg Get-Partition in this example: Get-Disk | Get-Partition | Where {$_} + var indexOfPreviousCommand = pipeline.PipelineElements.IndexOf(cmdAst) - 1; + if (indexOfPreviousCommand >= 0) + { + parent = pipeline.PipelineElements[indexOfPreviousCommand]; + break; + } } - - return; } - if (parent.Parent is CommandAst commandAst) - { - // We found a command, see if there is a previous command in the pipeline. - PipelineAst pipelineAst = (PipelineAst)commandAst.Parent; - var previousCommandIndex = pipelineAst.PipelineElements.IndexOf(commandAst) - 1; - if (previousCommandIndex < 0) - { - return; - } + parent = parent.Parent; + } - foreach (var result in InferTypes(pipelineAst.PipelineElements[0])) + if (parent is CatchClauseAst catchBlock) + { + if (catchBlock.CatchTypes.Count > 0) + { + foreach (TypeConstraintAst catchType in catchBlock.CatchTypes) { - if (result.Type != null) + Type exceptionType = catchType.TypeName.GetReflectionType(); + if (typeof(Exception).IsAssignableFrom(exceptionType)) { - // Assume (because we're looking at $_ and we're inside a script block that is an - // argument to some command) that the type we're getting is actually unrolled. - // This might not be right in all cases, but with our simple analysis, it's - // right more often than it's wrong. - if (result.Type.IsArray) - { - inferredTypes.Add(new PSTypeName(result.Type.GetElementType())); - continue; - } - - if (typeof(IEnumerable).IsAssignableFrom(result.Type)) - { - // We can't deduce much from IEnumerable, but we can if it's generic. - var enumerableInterfaces = result.Type.GetInterfaces(); - foreach (var t in enumerableInterfaces) - { - if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)) - { - inferredTypes.Add(new PSTypeName(t.GetGenericArguments()[0])); - } - } - - continue; - } + inferredTypes.Add(new PSTypeName(typeof(ErrorRecord<>).MakeGenericType(exceptionType))); } - - inferredTypes.Add(result); } + } - return; + // Either no type constraint was specified, or all the specified catch types were unavailable but we still know it's an error record. + if (inferredTypes.Count == 0) + { + inferredTypes.Add(new PSTypeName(typeof(ErrorRecord))); } } + else if (parent is TrapStatementAst trap) + { + if (trap.TrapType is not null) + { + Type exceptionType = trap.TrapType.TypeName.GetReflectionType(); + if (typeof(Exception).IsAssignableFrom(exceptionType)) + { + inferredTypes.Add(new PSTypeName(typeof(ErrorRecord<>).MakeGenericType(exceptionType))); + } + } + if (inferredTypes.Count == 0) + { + inferredTypes.Add(new PSTypeName(typeof(ErrorRecord))); + } + } + else if (parent is not null) + { + AddInferredTypesForDollarUnderbar(parent, inferredTypes); + } + + return; } // For certain variables, we always know their type, well at least we can assume we know. @@ -1918,14 +1972,19 @@ private void InferTypeFrom(VariableExpressionAst variableExpressionAst, List results) + { + foreach (var result in InferTypes(parentExpression)) + { + if (result.Type != null) + { + // Assume (because we're looking at $_ and we're inside a script block that is an + // argument to some command) that the type we're getting is actually unrolled. + // This might not be right in all cases, but with our simple analysis, it's + // right more often than it's wrong. + if (result.Type.IsArray) + { + results.Add(new PSTypeName(result.Type.GetElementType())); + continue; + } + + if (result.Type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(result.Type)) + { + // We can't deduce much from IEnumerable, but we can if it's generic. + var enumerableInterfaces = result.Type.GetInterfaces(); + foreach (var t in enumerableInterfaces) + { + if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + results.Add(new PSTypeName(t.GetGenericArguments()[0])); + } + } + + continue; + } + } + + results.Add(result); + } + } + /// /// Gets the most specific array type possible from a group of inferred types. /// @@ -2202,6 +2297,17 @@ private IEnumerable InferTypeFrom(IndexExpressionAst indexExpression continue; } + + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IList<>)) + { + var valueType = type.GetGenericArguments()[0]; + if (!valueType.ContainsGenericParameters) + { + foundAny = true; + yield return new PSTypeName(valueType); + } + continue; + } foreach (var iface in type.GetInterfaces()) { @@ -2356,9 +2462,10 @@ public static IEnumerable GetGetterProperty(this Type type, string p foreach (var m in type.GetMethods(BindingFlags.Public | BindingFlags.Instance)) { var name = m.Name; + // Equals without string allocation if (name.Length == propertyName.Length + 4 && name.StartsWith("get_") - && propertyName.IndexOf(name, 4, StringComparison.Ordinal) == 4) + && name.IndexOf(propertyName, 4, StringComparison.Ordinal) == 4) { res.Add(m); } diff --git a/src/System.Management.Automation/engine/parser/TypeResolver.cs b/src/System.Management.Automation/engine/parser/TypeResolver.cs index dd69985bd5f..e93719a3fb4 100644 --- a/src/System.Management.Automation/engine/parser/TypeResolver.cs +++ b/src/System.Management.Automation/engine/parser/TypeResolver.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Specialized; #if !UNIX using System.DirectoryServices; #endif @@ -370,10 +371,7 @@ internal static Type ResolveTypeNameWithContext(TypeName typeName, out Exception return typeName._typeDefinitionAst.Type; } - if (context == null) - { - context = LocalPipeline.GetExecutionContextFromTLS(); - } + context ??= LocalPipeline.GetExecutionContextFromTLS(); // Use the explicitly passed-in assembly list when it's specified by the caller. // Otherwise, retrieve all currently loaded assemblies. @@ -582,10 +580,7 @@ private TypeResolutionState(TypeResolutionState other, HashSet typesDefi internal static TypeResolutionState GetDefaultUsingState(ExecutionContext context) { - if (context == null) - { - context = LocalPipeline.GetExecutionContextFromTLS(); - } + context ??= LocalPipeline.GetExecutionContextFromTLS(); if (context != null) { @@ -759,6 +754,7 @@ internal static class CoreTypes { typeof(OutputTypeAttribute), new[] { "OutputType" } }, { typeof(object[]), null }, { typeof(ObjectSecurity), new[] { "ObjectSecurity" } }, + { typeof(OrderedDictionary), new[] { "ordered" } }, { typeof(ParameterAttribute), new[] { "Parameter" } }, { typeof(PhysicalAddress), new[] { "PhysicalAddress" } }, { typeof(PSCredential), new[] { "pscredential" } }, @@ -933,10 +929,7 @@ public static void Add(string typeName, Type type) public static bool Remove(string typeName) { userTypeAccelerators.Remove(typeName); - if (s_allTypeAccelerators != null) - { - s_allTypeAccelerators.Remove(typeName); - } + s_allTypeAccelerators?.Remove(typeName); return true; } diff --git a/src/System.Management.Automation/engine/parser/VariableAnalysis.cs b/src/System.Management.Automation/engine/parser/VariableAnalysis.cs index b38954ebfc6..8bf14d79aac 100644 --- a/src/System.Management.Automation/engine/parser/VariableAnalysis.cs +++ b/src/System.Management.Automation/engine/parser/VariableAnalysis.cs @@ -185,8 +185,7 @@ private void VisitParameters(ReadOnlyCollection parameters) // valuetype because the parameter has no value yet. For example: // & { param([System.Reflection.MemberTypes]$m) ($null -eq $m) } - object unused; - if (!Compiler.TryGetDefaultParameterValue(analysisDetails.Type, out unused)) + if (!Compiler.TryGetDefaultParameterValue(analysisDetails.Type, out _)) { analysisDetails.LocalTupleIndex = VariableAnalysis.ForceDynamic; } @@ -945,25 +944,11 @@ public object VisitScriptBlock(ScriptBlockAst scriptBlockAst) { _currentBlock = _entryBlock; - if (scriptBlockAst.DynamicParamBlock != null) - { - scriptBlockAst.DynamicParamBlock.Accept(this); - } - - if (scriptBlockAst.BeginBlock != null) - { - scriptBlockAst.BeginBlock.Accept(this); - } - - if (scriptBlockAst.ProcessBlock != null) - { - scriptBlockAst.ProcessBlock.Accept(this); - } - - if (scriptBlockAst.EndBlock != null) - { - scriptBlockAst.EndBlock.Accept(this); - } + scriptBlockAst.DynamicParamBlock?.Accept(this); + scriptBlockAst.BeginBlock?.Accept(this); + scriptBlockAst.ProcessBlock?.Accept(this); + scriptBlockAst.EndBlock?.Accept(this); + scriptBlockAst.CleanBlock?.Accept(this); _currentBlock.FlowsTo(_exitBlock); @@ -1317,10 +1302,7 @@ public object VisitDoUntilStatement(DoUntilStatementAst doUntilStatementAst) public object VisitForStatement(ForStatementAst forStatementAst) { - if (forStatementAst.Initializer != null) - { - forStatementAst.Initializer.Accept(this); - } + forStatementAst.Initializer?.Accept(this); var generateCondition = forStatementAst.Condition != null ? () => forStatementAst.Condition.Accept(this) @@ -1468,10 +1450,7 @@ public object VisitContinueStatement(ContinueStatementAst continueStatementAst) private Block ControlFlowStatement(PipelineBaseAst pipelineAst) { - if (pipelineAst != null) - { - pipelineAst.Accept(this); - } + pipelineAst?.Accept(this); _currentBlock.FlowsTo(_exitBlock); var lastBlockInStatement = _currentBlock; @@ -1642,11 +1621,7 @@ public object VisitCommandExpression(CommandExpressionAst commandExpressionAst) public object VisitCommandParameter(CommandParameterAst commandParameterAst) { - if (commandParameterAst.Argument != null) - { - commandParameterAst.Argument.Accept(this); - } - + commandParameterAst.Argument?.Accept(this); return null; } diff --git a/src/System.Management.Automation/engine/parser/ast.cs b/src/System.Management.Automation/engine/parser/ast.cs index 3e1f9ce60a5..54424ea4cc7 100644 --- a/src/System.Management.Automation/engine/parser/ast.cs +++ b/src/System.Management.Automation/engine/parser/ast.cs @@ -818,6 +818,46 @@ public ScriptBlockAst(IScriptExtent extent, NamedBlockAst processBlock, NamedBlockAst endBlock, NamedBlockAst dynamicParamBlock) + : this( + extent, + usingStatements, + attributes, + paramBlock, + beginBlock, + processBlock, + endBlock, + cleanBlock: null, + dynamicParamBlock) + { + } + + /// + /// Initializes a new instance of the class. + /// This construction uses explicitly named begin/process/end/clean blocks. + /// + /// The extent of the script block. + /// The list of using statments, may be null. + /// The set of attributes for the script block. + /// The ast for the param block, may be null. + /// The ast for the begin block, may be null. + /// The ast for the process block, may be null. + /// The ast for the end block, may be null. + /// The ast for the clean block, may be null. + /// The ast for the dynamicparam block, may be null. + /// + /// If is null. + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "param")] + public ScriptBlockAst( + IScriptExtent extent, + IEnumerable usingStatements, + IEnumerable attributes, + ParamBlockAst paramBlock, + NamedBlockAst beginBlock, + NamedBlockAst processBlock, + NamedBlockAst endBlock, + NamedBlockAst cleanBlock, + NamedBlockAst dynamicParamBlock) : base(extent) { SetUsingStatements(usingStatements); @@ -856,6 +896,12 @@ public ScriptBlockAst(IScriptExtent extent, SetParent(endBlock); } + if (cleanBlock != null) + { + this.CleanBlock = cleanBlock; + SetParent(cleanBlock); + } + if (dynamicParamBlock != null) { this.DynamicParamBlock = dynamicParamBlock; @@ -888,6 +934,35 @@ public ScriptBlockAst(IScriptExtent extent, { } + /// + /// Initializes a new instance of the class. + /// This construction uses explicitly named begin/process/end/clean blocks. + /// + /// The extent of the script block. + /// The list of using statments, may be null. + /// The ast for the param block, may be null. + /// The ast for the begin block, may be null. + /// The ast for the process block, may be null. + /// The ast for the end block, may be null. + /// The ast for the clean block, may be null. + /// The ast for the dynamicparam block, may be null. + /// + /// If is null. + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "param")] + public ScriptBlockAst( + IScriptExtent extent, + IEnumerable usingStatements, + ParamBlockAst paramBlock, + NamedBlockAst beginBlock, + NamedBlockAst processBlock, + NamedBlockAst endBlock, + NamedBlockAst cleanBlock, + NamedBlockAst dynamicParamBlock) + : this(extent, usingStatements, null, paramBlock, beginBlock, processBlock, endBlock, cleanBlock, dynamicParamBlock) + { + } + /// /// Construct a ScriptBlockAst that uses explicitly named begin/process/end blocks. /// @@ -911,6 +986,33 @@ public ScriptBlockAst(IScriptExtent extent, { } + /// + /// Initializes a new instance of the class. + /// This construction uses explicitly named begin/process/end/clean blocks. + /// + /// The extent of the script block. + /// The ast for the param block, may be null. + /// The ast for the begin block, may be null. + /// The ast for the process block, may be null. + /// The ast for the end block, may be null. + /// The ast for the clean block, may be null. + /// The ast for the dynamicparam block, may be null. + /// + /// If is null. + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "param")] + public ScriptBlockAst( + IScriptExtent extent, + ParamBlockAst paramBlock, + NamedBlockAst beginBlock, + NamedBlockAst processBlock, + NamedBlockAst endBlock, + NamedBlockAst cleanBlock, + NamedBlockAst dynamicParamBlock) + : this(extent, null, paramBlock, beginBlock, processBlock, endBlock, cleanBlock, dynamicParamBlock) + { + } + /// /// Construct a ScriptBlockAst that does not use explicitly named blocks. /// @@ -1115,6 +1217,11 @@ private void SetUsingStatements(IEnumerable usingStatements) /// public NamedBlockAst EndBlock { get; } + /// + /// Gets the ast representing the clean block for a script block, or null if no clean block was specified. + /// + public NamedBlockAst CleanBlock { get; } + /// /// The ast representing the dynamicparam block for a script block, or null if no dynamicparam block was specified. /// @@ -1194,17 +1301,25 @@ public override Ast Copy() var newBeginBlock = CopyElement(this.BeginBlock); var newProcessBlock = CopyElement(this.ProcessBlock); var newEndBlock = CopyElement(this.EndBlock); + var newCleanBlock = CopyElement(this.CleanBlock); var newDynamicParamBlock = CopyElement(this.DynamicParamBlock); var newAttributes = CopyElements(this.Attributes); var newUsingStatements = CopyElements(this.UsingStatements); - var scriptBlockAst = new ScriptBlockAst(this.Extent, newUsingStatements, newAttributes, newParamBlock, newBeginBlock, newProcessBlock, - newEndBlock, newDynamicParamBlock) + return new ScriptBlockAst( + this.Extent, + newUsingStatements, + newAttributes, + newParamBlock, + newBeginBlock, + newProcessBlock, + newEndBlock, + newCleanBlock, + newDynamicParamBlock) { IsConfiguration = this.IsConfiguration, ScriptRequirements = this.ScriptRequirements }; - return scriptBlockAst; } internal string ToStringForSerialization() @@ -1261,7 +1376,8 @@ internal string ToStringForSerialization(Tuple, stri var varAst = ast as VariableExpressionAst; if (varAst != null) { - string varName = varAst.VariablePath.UserPath; + VariablePath varPath = varAst.VariablePath; + string varName = varPath.IsDriveQualified ? $"{varPath.DriveName}_{varPath.UnqualifiedPath}" : $"{varPath.UnqualifiedPath}"; string varSign = varAst.Splatted ? "@" : "$"; string newVarName = varSign + UsingExpressionAst.UsingPrefix + varName; @@ -1366,17 +1482,27 @@ internal override AstVisitAction InternalVisit(AstVisitor visitor) } } - if (action == AstVisitAction.Continue && ParamBlock != null) - action = ParamBlock.InternalVisit(visitor); - if (action == AstVisitAction.Continue && DynamicParamBlock != null) - action = DynamicParamBlock.InternalVisit(visitor); - if (action == AstVisitAction.Continue && BeginBlock != null) - action = BeginBlock.InternalVisit(visitor); - if (action == AstVisitAction.Continue && ProcessBlock != null) - action = ProcessBlock.InternalVisit(visitor); - if (action == AstVisitAction.Continue && EndBlock != null) - action = EndBlock.InternalVisit(visitor); + if (action == AstVisitAction.Continue) + { + _ = VisitAndShallContinue(ParamBlock) && + VisitAndShallContinue(DynamicParamBlock) && + VisitAndShallContinue(BeginBlock) && + VisitAndShallContinue(ProcessBlock) && + VisitAndShallContinue(EndBlock) && + VisitAndShallContinue(CleanBlock); + } + return visitor.CheckForPostAction(this, action); + + bool VisitAndShallContinue(Ast ast) + { + if (ast is not null) + { + action = ast.InternalVisit(visitor); + } + + return action == AstVisitAction.Continue; + } } #endregion Visitors @@ -1513,9 +1639,7 @@ Tuple IParameterMetadataProvider.GetWithInputHandlingForInvokeCo private string GetWithInputHandlingForInvokeCommandImpl(Tuple, string> usingVariablesTuple) { // do not add "$input |" to complex pipelines - string unused1; - string unused2; - var pipelineAst = GetSimplePipeline(false, out unused1, out unused2); + var pipelineAst = GetSimplePipeline(false, out _, out _); if (pipelineAst == null) { return (usingVariablesTuple == null) @@ -1580,9 +1704,12 @@ bool IParameterMetadataProvider.UsesCmdletBinding() internal PipelineAst GetSimplePipeline(bool allowMultiplePipelines, out string errorId, out string errorMsg) { - if (BeginBlock != null || ProcessBlock != null || DynamicParamBlock != null) + if (BeginBlock != null + || ProcessBlock != null + || CleanBlock != null + || DynamicParamBlock != null) { - errorId = "CanConvertOneClauseOnly"; + errorId = nameof(AutomationExceptions.CanConvertOneClauseOnly); errorMsg = AutomationExceptions.CanConvertOneClauseOnly; return null; } @@ -1748,7 +1875,7 @@ internal static bool UsesCmdletBinding(IEnumerable parameters) public class NamedBlockAst : Ast { /// - /// Construct the ast for a begin, process, end, or dynamic param block. + /// Construct the ast for a begin, process, end, clean, or dynamic param block. /// /// /// The extent of the block. If is false, the extent includes @@ -1760,6 +1887,7 @@ public class NamedBlockAst : Ast /// /// /// + /// /// /// /// @@ -1778,8 +1906,7 @@ public NamedBlockAst(IScriptExtent extent, TokenKind blockName, StatementBlockAs { // Validate the block name. If the block is unnamed, it must be an End block (for a function) // or Process block (for a filter). - if (!blockName.HasTrait(TokenFlags.ScriptBlockBlockName) - || (unnamed && (blockName == TokenKind.Begin || blockName == TokenKind.Dynamicparam))) + if (HasInvalidBlockName(blockName, unnamed)) { throw PSTraceSource.NewArgumentException(nameof(blockName)); } @@ -1837,6 +1964,7 @@ public NamedBlockAst(IScriptExtent extent, TokenKind blockName, StatementBlockAs /// /// /// + /// /// /// /// @@ -1876,6 +2004,14 @@ public override Ast Copy() return new NamedBlockAst(this.Extent, this.BlockKind, statementBlock, this.Unnamed); } + private static bool HasInvalidBlockName(TokenKind blockName, bool unnamed) + { + return !blockName.HasTrait(TokenFlags.ScriptBlockBlockName) + || (unnamed + && blockName != TokenKind.Process + && blockName != TokenKind.End); + } + // Used by the debugger for command breakpoints internal IScriptExtent OpenCurlyExtent { get; } @@ -2329,7 +2465,8 @@ internal string GetParamTextWithDollarUsingHandling(IEnumerator= endOffset) { break; } - string varName = varAst.VariablePath.UserPath; + VariablePath varPath = varAst.VariablePath; + string varName = varPath.IsDriveQualified ? $"{varPath.DriveName}_{varPath.UnqualifiedPath}" : $"{varPath.UnqualifiedPath}"; string varSign = varAst.Splatted ? "@" : "$"; string newVarName = varSign + UsingExpressionAst.UsingPrefix + varName; @@ -3330,6 +3467,8 @@ public bool IsConstructor internal IScriptExtent NameExtent { get { return _functionDefinitionAst.NameExtent; } } + private string _toolTip; + /// /// Copy a function member ast. /// @@ -3344,28 +3483,56 @@ public override Ast Copy() internal override string GetTooltip() { - var sb = new StringBuilder(); - if (IsStatic) + if (!string.IsNullOrEmpty(_toolTip)) { - sb.Append("static "); + return _toolTip; } - sb.Append(IsReturnTypeVoid() ? "void" : ReturnType.TypeName.FullName); - sb.Append(' '); - sb.Append(Name); - sb.Append('('); - for (int i = 0; i < Parameters.Count; i++) + var sb = new StringBuilder(); + var classMembers = ((TypeDefinitionAst)Parent).Members; + for (int i = 0; i < classMembers.Count; i++) { - if (i > 0) + var methodMember = classMembers[i] as FunctionMemberAst; + if (methodMember is null || + !Name.Equals(methodMember.Name) || + IsStatic != methodMember.IsStatic) + { + continue; + } + + if (sb.Length > 0) + { + sb.AppendLine(); + } + + if (methodMember.IsStatic) + { + sb.Append("static "); + } + + if (!methodMember.IsConstructor) { - sb.Append(", "); + sb.Append(methodMember.IsReturnTypeVoid() ? "void" : methodMember.ReturnType.TypeName.FullName); + sb.Append(' '); } - sb.Append(Parameters[i].GetTooltip()); + sb.Append(methodMember.Name); + sb.Append('('); + for (int j = 0; j < methodMember.Parameters.Count; j++) + { + if (j > 0) + { + sb.Append(", "); + } + + sb.Append(methodMember.Parameters[j].GetTooltip()); + } + + sb.Append(')'); } - sb.Append(')'); - return sb.ToString(); + _toolTip = sb.ToString(); + return _toolTip; } #region Visitors @@ -7862,7 +8029,7 @@ internal bool IsRef() public class MemberExpressionAst : ExpressionAst, ISupportsAssignment { /// - /// Construct an ast to reference a property. + /// Initializes a new instance of the class. /// /// /// The extent of the expression, starting with the expression before the operator '.' or '::' and ending after @@ -7938,7 +8105,13 @@ public override Ast Copy() { var newExpression = CopyElement(this.Expression); var newMember = CopyElement(this.Member); - return new MemberExpressionAst(this.Extent, newExpression, newMember, this.Static, this.NullConditional); + + return new MemberExpressionAst( + this.Extent, + newExpression, + newMember, + this.Static, + this.NullConditional); } #region Visitors @@ -7974,7 +8147,7 @@ IAssignableValue ISupportsAssignment.GetAssignableValue() public class InvokeMemberExpressionAst : MemberExpressionAst, ISupportsAssignment { /// - /// Construct an instance of a method invocation expression. + /// Initializes a new instance of the class. /// /// /// The extent of the expression, starting with the expression before the invocation operator and ending with the @@ -7986,10 +8159,17 @@ public class InvokeMemberExpressionAst : MemberExpressionAst, ISupportsAssignmen /// /// True if the invocation is for a static method, using '::', false if invoking a method on an instance using '.'. /// + /// The generic type arguments passed to the method. /// /// If is null. /// - public InvokeMemberExpressionAst(IScriptExtent extent, ExpressionAst expression, CommandElementAst method, IEnumerable arguments, bool @static) + public InvokeMemberExpressionAst( + IScriptExtent extent, + ExpressionAst expression, + CommandElementAst method, + IEnumerable arguments, + bool @static, + IList genericTypes) : base(extent, expression, method, @static) { if (arguments != null && arguments.Any()) @@ -7997,6 +8177,37 @@ public InvokeMemberExpressionAst(IScriptExtent extent, ExpressionAst expression, this.Arguments = new ReadOnlyCollection(arguments.ToArray()); SetParents(Arguments); } + + if (genericTypes != null && genericTypes.Count > 0) + { + this.GenericTypeArguments = new ReadOnlyCollection(genericTypes); + } + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The extent of the expression, starting with the expression before the invocation operator and ending with the + /// closing paren after the arguments. + /// + /// The expression before the invocation operator ('.', '::'). + /// The method to invoke. + /// The arguments to pass to the method. + /// + /// True if the invocation is for a static method, using '::', false if invoking a method on an instance using '.'. + /// + /// + /// If is null. + /// + public InvokeMemberExpressionAst( + IScriptExtent extent, + ExpressionAst expression, + CommandElementAst method, + IEnumerable arguments, + bool @static) + : this(extent, expression, method, arguments, @static, genericTypes: null) + { } /// @@ -8013,15 +8224,56 @@ public InvokeMemberExpressionAst(IScriptExtent extent, ExpressionAst expression, /// True if the invocation is for a static method, using '::', false if invoking a method on an instance using '.' or '?.'. /// /// True if the operator used is '?.'. + /// The generic type arguments passed to the method. /// /// If is null. /// - public InvokeMemberExpressionAst(IScriptExtent extent, ExpressionAst expression, CommandElementAst method, IEnumerable arguments, bool @static, bool nullConditional) - : this(extent, expression, method, arguments, @static) + public InvokeMemberExpressionAst( + IScriptExtent extent, + ExpressionAst expression, + CommandElementAst method, + IEnumerable arguments, + bool @static, + bool nullConditional, + IList genericTypes) + : this(extent, expression, method, arguments, @static, genericTypes) { this.NullConditional = nullConditional; } + /// + /// Initializes a new instance of the class. + /// + /// + /// The extent of the expression, starting with the expression before the invocation operator and ending with the + /// closing paren after the arguments. + /// + /// The expression before the invocation operator ('.', '::' or '?.'). + /// The method to invoke. + /// The arguments to pass to the method. + /// + /// True if the invocation is for a static method, using '::', false if invoking a method on an instance using '.' or '?.'. + /// + /// True if the operator used is '?.'. + /// + /// If is null. + /// + public InvokeMemberExpressionAst( + IScriptExtent extent, + ExpressionAst expression, + CommandElementAst method, + IEnumerable arguments, + bool @static, + bool nullConditional) + : this(extent, expression, method, arguments, @static, nullConditional, genericTypes: null) + { + } + + /// + /// Gets a list of generic type arguments passed to this method invocation. + /// + public ReadOnlyCollection GenericTypeArguments { get; } + /// /// The non-empty collection of arguments to pass when invoking the method, or null if no arguments were specified. /// @@ -8035,7 +8287,15 @@ public override Ast Copy() var newExpression = CopyElement(this.Expression); var newMethod = CopyElement(this.Member); var newArguments = CopyElements(this.Arguments); - return new InvokeMemberExpressionAst(this.Extent, newExpression, newMethod, newArguments, this.Static, this.NullConditional); + + return new InvokeMemberExpressionAst( + this.Extent, + newExpression, + newMethod, + newArguments, + this.Static, + this.NullConditional, + this.GenericTypeArguments); } #region Visitors diff --git a/src/System.Management.Automation/engine/parser/token.cs b/src/System.Management.Automation/engine/parser/token.cs index 893ec9fc8e0..43a3e86ebb7 100644 --- a/src/System.Management.Automation/engine/parser/token.cs +++ b/src/System.Management.Automation/engine/parser/token.cs @@ -588,6 +588,9 @@ public enum TokenKind /// The 'default' keyword Default = 169, + /// The 'clean' keyword. + Clean = 170, + #endregion Keywords } @@ -659,7 +662,7 @@ public enum TokenFlags Keyword = 0x00000010, /// - /// The token one of the keywords that is a part of a script block: 'begin', 'process', 'end', or 'dynamicparam'. + /// The token is one of the keywords that is a part of a script block: 'begin', 'process', 'end', 'clean', or 'dynamicparam'. /// ScriptBlockBlockName = 0x00000020, @@ -948,6 +951,7 @@ public static class TokenTraits /* Hidden */ TokenFlags.Keyword, /* Base */ TokenFlags.Keyword, /* Default */ TokenFlags.Keyword, + /* Clean */ TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName, #endregion Flags for keywords }; @@ -1147,6 +1151,7 @@ public static class TokenTraits /* Hidden */ "hidden", /* Base */ "base", /* Default */ "default", + /* Clean */ "clean", #endregion Text for keywords }; @@ -1154,10 +1159,12 @@ public static class TokenTraits #if DEBUG static TokenTraits() { - Diagnostics.Assert(s_staticTokenFlags.Length == ((int)TokenKind.Default + 1), - "Table size out of sync with enum - _staticTokenFlags"); - Diagnostics.Assert(s_tokenText.Length == ((int)TokenKind.Default + 1), - "Table size out of sync with enum - _tokenText"); + Diagnostics.Assert( + s_staticTokenFlags.Length == ((int)TokenKind.Clean + 1), + "Table size out of sync with enum - _staticTokenFlags"); + Diagnostics.Assert( + s_tokenText.Length == ((int)TokenKind.Clean + 1), + "Table size out of sync with enum - _tokenText"); // Some random assertions to make sure the enum and the traits are in sync Diagnostics.Assert(GetTraits(TokenKind.Begin) == (TokenFlags.Keyword | TokenFlags.ScriptBlockBlockName), "Table out of sync with enum - flags Begin"); diff --git a/src/System.Management.Automation/engine/parser/tokenizer.cs b/src/System.Management.Automation/engine/parser/tokenizer.cs index 161e53cef0c..9e133016e85 100644 --- a/src/System.Management.Automation/engine/parser/tokenizer.cs +++ b/src/System.Management.Automation/engine/parser/tokenizer.cs @@ -635,23 +635,23 @@ private static readonly Dictionary s_operatorTable /*A*/ "configuration", "public", "private", "static", /*A*/ /*B*/ "interface", "enum", "namespace", "module", /*B*/ /*C*/ "type", "assembly", "command", "hidden", /*C*/ - /*D*/ "base", "default", /*D*/ + /*D*/ "base", "default", "clean", /*D*/ }; private static readonly TokenKind[] s_keywordTokenKind = new TokenKind[] { - /*1*/ TokenKind.ElseIf, TokenKind.If, TokenKind.Else, TokenKind.Switch, /*1*/ - /*2*/ TokenKind.Foreach, TokenKind.From, TokenKind.In, TokenKind.For, /*2*/ - /*3*/ TokenKind.While, TokenKind.Until, TokenKind.Do, TokenKind.Try, /*3*/ - /*4*/ TokenKind.Catch, TokenKind.Finally, TokenKind.Trap, TokenKind.Data, /*4*/ - /*5*/ TokenKind.Return, TokenKind.Continue, TokenKind.Break, TokenKind.Exit, /*5*/ - /*6*/ TokenKind.Throw, TokenKind.Begin, TokenKind.Process, TokenKind.End, /*6*/ - /*7*/ TokenKind.Dynamicparam, TokenKind.Function, TokenKind.Filter, TokenKind.Param, /*7*/ - /*8*/ TokenKind.Class, TokenKind.Define, TokenKind.Var, TokenKind.Using, /*8*/ - /*9*/ TokenKind.Workflow, TokenKind.Parallel, TokenKind.Sequence, TokenKind.InlineScript, /*9*/ - /*A*/ TokenKind.Configuration, TokenKind.Public, TokenKind.Private, TokenKind.Static, /*A*/ - /*B*/ TokenKind.Interface, TokenKind.Enum, TokenKind.Namespace, TokenKind.Module, /*B*/ - /*C*/ TokenKind.Type, TokenKind.Assembly, TokenKind.Command, TokenKind.Hidden, /*C*/ - /*D*/ TokenKind.Base, TokenKind.Default, /*D*/ + /*1*/ TokenKind.ElseIf, TokenKind.If, TokenKind.Else, TokenKind.Switch, /*1*/ + /*2*/ TokenKind.Foreach, TokenKind.From, TokenKind.In, TokenKind.For, /*2*/ + /*3*/ TokenKind.While, TokenKind.Until, TokenKind.Do, TokenKind.Try, /*3*/ + /*4*/ TokenKind.Catch, TokenKind.Finally, TokenKind.Trap, TokenKind.Data, /*4*/ + /*5*/ TokenKind.Return, TokenKind.Continue, TokenKind.Break, TokenKind.Exit, /*5*/ + /*6*/ TokenKind.Throw, TokenKind.Begin, TokenKind.Process, TokenKind.End, /*6*/ + /*7*/ TokenKind.Dynamicparam, TokenKind.Function, TokenKind.Filter, TokenKind.Param, /*7*/ + /*8*/ TokenKind.Class, TokenKind.Define, TokenKind.Var, TokenKind.Using, /*8*/ + /*9*/ TokenKind.Workflow, TokenKind.Parallel, TokenKind.Sequence, TokenKind.InlineScript, /*9*/ + /*A*/ TokenKind.Configuration, TokenKind.Public, TokenKind.Private, TokenKind.Static, /*A*/ + /*B*/ TokenKind.Interface, TokenKind.Enum, TokenKind.Namespace, TokenKind.Module, /*B*/ + /*C*/ TokenKind.Type, TokenKind.Assembly, TokenKind.Command, TokenKind.Hidden, /*C*/ + /*D*/ TokenKind.Base, TokenKind.Default, TokenKind.Clean, /*D*/ }; internal static readonly string[] _operatorText = new string[] { @@ -1218,10 +1218,7 @@ private Token NewCommentToken() private T SaveToken(T token) where T : Token { - if (TokenList != null) - { - TokenList.Add(token); - } + TokenList?.Add(token); // Keep track of the first and last token even if we're not saving tokens // for the special variables $$ and $^. @@ -1234,10 +1231,7 @@ private T SaveToken(T token) where T : Token // Don't remember these tokens, they aren't useful in $$ and $^. break; default: - if (FirstToken == null) - { - FirstToken = token; - } + FirstToken ??= token; LastToken = token; break; @@ -1807,8 +1801,7 @@ private void ScanLineComment() } else if (matchedRequires && _nestedTokensAdjustment == 0) { - if (RequiresTokens == null) - RequiresTokens = new List(); + RequiresTokens ??= new List(); RequiresTokens.Add(token); } } @@ -1945,10 +1938,7 @@ internal ScriptRequirements GetScriptRequirements() PSSnapinToken.StartsWith(parameter.ParameterName, StringComparison.OrdinalIgnoreCase)) { snapinSpecified = true; - if (requiredSnapins == null) - { - requiredSnapins = new List(); - } + requiredSnapins ??= new List(); break; } @@ -2214,8 +2204,7 @@ private void HandleRequiresParameter(CommandParameterAst parameter, return; } - if (requiredModules == null) - requiredModules = new List(); + requiredModules ??= new List(); requiredModules.Add(moduleSpecification); } } @@ -2238,8 +2227,7 @@ private List HandleRequiresAssemblyArgument(Ast argumentAst, object arg, } else { - if (requiredAssemblies == null) - requiredAssemblies = new List(); + requiredAssemblies ??= new List(); if (!requiredAssemblies.Contains((string)arg)) { @@ -2261,8 +2249,7 @@ private List HandleRequiresPSEditionArgument(Ast argumentAst, object arg } else { - if (requiredEditions == null) - requiredEditions = new List(); + requiredEditions ??= new List(); var edition = (string)arg; if (!Utils.IsValidPSEditionValue(edition)) diff --git a/src/System.Management.Automation/engine/pipeline.cs b/src/System.Management.Automation/engine/pipeline.cs index 58ea07de6ae..3373b5e56f0 100644 --- a/src/System.Management.Automation/engine/pipeline.cs +++ b/src/System.Management.Automation/engine/pipeline.cs @@ -66,7 +66,6 @@ internal class PipelineProcessor : IDisposable public void Dispose() { Dispose(true); - GC.SuppressFinalize(this); } private void Dispose(bool disposing) @@ -214,10 +213,7 @@ private void Log(string logElement, InvocationInfo invocation, PipelineExecution // Log the cmdlet invocation execution details if we didn't have an associated script line with it. if ((invocation == null) || string.IsNullOrEmpty(invocation.Line)) { - if (hostInterface != null) - { - hostInterface.TranscribeCommand(logElement, invocation); - } + hostInterface?.TranscribeCommand(logElement, invocation); } if (_needToLog && !string.IsNullOrEmpty(logElement)) @@ -267,9 +263,12 @@ internal int Add(CommandProcessorBase commandProcessor) internal void AddRedirectionPipe(PipelineProcessor pipelineProcessor) { - if (pipelineProcessor == null) throw PSTraceSource.NewArgumentNullException(nameof(pipelineProcessor)); - if (_redirectionPipes == null) - _redirectionPipes = new List(); + if (pipelineProcessor is null) + { + throw PSTraceSource.NewArgumentNullException(nameof(pipelineProcessor)); + } + + _redirectionPipes ??= new List(); _redirectionPipes.Add(pipelineProcessor); } @@ -347,18 +346,15 @@ private int AddCommand(CommandProcessorBase commandProcessor, int readFromComman } else { - CommandProcessorBase prevcommandProcessor = _commands[readFromCommand - 1] as CommandProcessorBase; - if (prevcommandProcessor == null || prevcommandProcessor.CommandRuntime == null) - { - // "PipelineProcessor.AddCommand(): previous request object == null" - throw PSTraceSource.NewInvalidOperationException(); - } + var prevcommandProcessor = _commands[readFromCommand - 1] as CommandProcessorBase; + ValidateCommandProcessorNotNull(prevcommandProcessor, errorMessage: null); + + Pipe UpstreamPipe = (readErrorQueue) + ? prevcommandProcessor.CommandRuntime.ErrorOutputPipe + : prevcommandProcessor.CommandRuntime.OutputPipe; - Pipe UpstreamPipe = (readErrorQueue) ? - prevcommandProcessor.CommandRuntime.ErrorOutputPipe : prevcommandProcessor.CommandRuntime.OutputPipe; if (UpstreamPipe == null) { - // "PipelineProcessor.AddCommand(): UpstreamPipe == null" throw PSTraceSource.NewInvalidOperationException(); } @@ -381,11 +377,8 @@ private int AddCommand(CommandProcessorBase commandProcessor, int readFromComman for (int i = 0; i < _commands.Count; i++) { prevcommandProcessor = _commands[i]; - if (prevcommandProcessor == null || prevcommandProcessor.CommandRuntime == null) - { - // "PipelineProcessor.AddCommand(): previous request object == null" - throw PSTraceSource.NewInvalidOperationException(); - } + ValidateCommandProcessorNotNull(prevcommandProcessor, errorMessage: null); + // check whether the error output is already claimed if (prevcommandProcessor.CommandRuntime.ErrorOutputPipe.DownstreamCmdlet != null) continue; @@ -470,194 +463,305 @@ internal Array SynchronousExecuteEnumerate(object input) throw new PipelineStoppedException(); } - ExceptionDispatchInfo toRethrowInfo; + bool pipelineSucceeded = false; + ExceptionDispatchInfo toRethrowInfo = null; + CommandProcessorBase commandRequestingUpstreamCommandsToStop = null; + try { - CommandProcessorBase commandRequestingUpstreamCommandsToStop = null; try { - // If the caller specified an input object array, - // we run assuming there is an incoming "stream" - // of objects. This will prevent the one default call - // to ProcessRecord on the first command. - Start(input != AutomationNull.Value); + try + { + // If the caller specified an input object array, we run assuming there is an incoming "stream" + // of objects. This will prevent the one default call to ProcessRecord on the first command. + Start(incomingStream: input != AutomationNull.Value); + + // Start has already validated firstcommandProcessor + CommandProcessorBase firstCommandProcessor = _commands[0]; - // Start has already validated firstcommandProcessor - CommandProcessorBase firstCommandProcessor = _commands[0]; + // Add any input to the first command. + if (ExternalInput is not null) + { + firstCommandProcessor.CommandRuntime.InputPipe.ExternalReader = ExternalInput; + } - // Add any input to the first command. - if (ExternalInput != null) + Inject(input, enumerate: true); + } + catch (PipelineStoppedException) { - firstCommandProcessor.CommandRuntime.InputPipe.ExternalReader - = ExternalInput; + if (_firstTerminatingError?.SourceException is StopUpstreamCommandsException exception) + { + _firstTerminatingError = null; + commandRequestingUpstreamCommandsToStop = exception.RequestingCommandProcessor; + } + else + { + throw; + } } - Inject(input, enumerate: true); + DoCompleteCore(commandRequestingUpstreamCommandsToStop); + pipelineSucceeded = true; } - catch (PipelineStoppedException) + finally { - StopUpstreamCommandsException stopUpstreamCommandsException = - _firstTerminatingError != null - ? _firstTerminatingError.SourceException as StopUpstreamCommandsException - : null; - if (stopUpstreamCommandsException == null) - { - throw; - } - else - { - _firstTerminatingError = null; - commandRequestingUpstreamCommandsToStop = stopUpstreamCommandsException.RequestingCommandProcessor; - } + // Clean up resources for script commands, no matter the pipeline succeeded or not. + // This method catches and handles all exceptions inside, so it will never throw. + Clean(); } - DoCompleteCore(commandRequestingUpstreamCommandsToStop); - - // By this point, we are sure all commandProcessors hosted by the current pipelineProcess are done execution, - // so if there are any redirection pipelineProcessors associated with any of those commandProcessors, we should - // call DoComplete on them. - if (_redirectionPipes != null) + if (pipelineSucceeded) { - foreach (PipelineProcessor redirectPipelineProcessor in _redirectionPipes) + // Now, we are sure all 'commandProcessors' hosted by the current 'pipelineProcessor' are done execution, + // so if there are any redirection 'pipelineProcessors' associated with any of those 'commandProcessors', + // they must have successfully executed 'StartStepping' and 'Step', and thus we should call 'DoComplete' + // on them for completeness. + if (_redirectionPipes is not null) { - redirectPipelineProcessor.DoCompleteCore(null); + foreach (PipelineProcessor redirectPipelineProcessor in _redirectionPipes) + { + // The 'Clean' block for each 'commandProcessor' might still write to a pipe that is associated + // with the redirection 'pipelineProcessor' (e.g. a redirected error pipe), which would trigger + // the call to 'pipelineProcessor.Step'. + // It's possible (though very unlikely) that the call to 'pipelineProcessor.Step' failed with an + // exception, and in such case, the 'pipelineProcessor' would have been disposed, and therefore + // the call to 'DoComplete' will simply return, because '_commands' was already set to null. + redirectPipelineProcessor.DoCompleteCore(null); + } } - } - return RetrieveResults(); + // The 'Clean' blocks write nothing to the output pipe, so the results won't be affected by them. + return RetrieveResults(); + } } catch (RuntimeException e) { - // The error we want to report is the first terminating error - // which occurred during pipeline execution, regardless - // of whether other errors occurred afterward. - toRethrowInfo = _firstTerminatingError ?? ExceptionDispatchInfo.Capture(e); - this.LogExecutionException(toRethrowInfo.SourceException); - } - // NTRAID#Windows Out Of Band Releases-929020-2006/03/14-JonN - catch (System.Runtime.InteropServices.InvalidComObjectException comException) - { - // The error we want to report is the first terminating error - // which occurred during pipeline execution, regardless - // of whether other errors occurred afterward. - if (_firstTerminatingError != null) - { - toRethrowInfo = _firstTerminatingError; - } - else - { - string message = StringUtil.Format(ParserStrings.InvalidComObjectException, comException.Message); - var rte = new RuntimeException(message, comException); - rte.SetErrorId("InvalidComObjectException"); - toRethrowInfo = ExceptionDispatchInfo.Capture(rte); - } - - this.LogExecutionException(toRethrowInfo.SourceException); + toRethrowInfo = GetFirstError(e); } finally { DisposeCommands(); } - // By rethrowing the exception outside of the handler, - // we allow the CLR on X64/IA64 to free from the stack - // the exception records related to this exception. + // By rethrowing the exception outside of the handler, we allow the CLR on X64/IA64 to free from + // the stack the exception records related to this exception. - // The only reason we should get here is if - // an exception should be rethrown. + // The only reason we should get here is if an exception should be rethrown. Diagnostics.Assert(toRethrowInfo != null, "Alternate protocol path failure"); toRethrowInfo.Throw(); - return null; // UNREACHABLE + + // UNREACHABLE + return null; + } + + private ExceptionDispatchInfo GetFirstError(RuntimeException e) + { + // The error we want to report is the first terminating error which occurred during pipeline execution, + // regardless of whether other errors occurred afterward. + var firstError = _firstTerminatingError ?? ExceptionDispatchInfo.Capture(e); + LogExecutionException(firstError.SourceException); + return firstError; + } + + private void ThrowFirstErrorIfExisting(bool logException) + { + if (_firstTerminatingError != null) + { + if (logException) + { + LogExecutionException(_firstTerminatingError.SourceException); + } + + _firstTerminatingError.Throw(); + } } private void DoCompleteCore(CommandProcessorBase commandRequestingUpstreamCommandsToStop) { - // Call DoComplete() for all the commands. DoComplete() will internally call Complete() + if (_commands is null) + { + // This could happen to a redirection pipeline, either for an expression (e.g. 1 > a.txt) + // or for a command (e.g. command > a.txt). + // An exception may be thrown from the call to 'StartStepping' or 'Step' on the pipeline, + // which causes the pipeline commands to be disposed. + return; + } + + // Call DoComplete() for all the commands, which will internally call Complete() MshCommandRuntime lastCommandRuntime = null; - if (_commands != null) + for (int i = 0; i < _commands.Count; i++) { - for (int i = 0; i < _commands.Count; i++) - { - CommandProcessorBase commandProcessor = _commands[i]; + CommandProcessorBase commandProcessor = _commands[i]; - if (commandProcessor == null) - { - // "null command " + i - throw PSTraceSource.NewInvalidOperationException(); - } + if (commandProcessor is null) + { + // An internal error that should not happen. + throw PSTraceSource.NewInvalidOperationException(); + } - if (object.ReferenceEquals(commandRequestingUpstreamCommandsToStop, commandProcessor)) - { - commandRequestingUpstreamCommandsToStop = null; - continue; // do not call DoComplete/EndProcessing on the command that initiated stopping - } + if (object.ReferenceEquals(commandRequestingUpstreamCommandsToStop, commandProcessor)) + { + // Do not call DoComplete/EndProcessing on the command that initiated stopping. + commandRequestingUpstreamCommandsToStop = null; + continue; + } - if (commandRequestingUpstreamCommandsToStop != null) - { - continue; // do not call DoComplete/EndProcessing on commands that were stopped upstream - } + if (commandRequestingUpstreamCommandsToStop is not null) + { + // Do not call DoComplete/EndProcessing on commands that were stopped upstream. + continue; + } - try + try + { + commandProcessor.DoComplete(); + } + catch (PipelineStoppedException) + { + if (_firstTerminatingError?.SourceException is StopUpstreamCommandsException exception) { - commandProcessor.DoComplete(); + _firstTerminatingError = null; + commandRequestingUpstreamCommandsToStop = exception.RequestingCommandProcessor; } - catch (PipelineStoppedException) + else { - StopUpstreamCommandsException stopUpstreamCommandsException = - _firstTerminatingError != null - ? _firstTerminatingError.SourceException as StopUpstreamCommandsException - : null; - if (stopUpstreamCommandsException == null) - { - throw; - } - else - { - _firstTerminatingError = null; - commandRequestingUpstreamCommandsToStop = stopUpstreamCommandsException.RequestingCommandProcessor; - } + throw; } + } - EtwActivity.SetActivityId(commandProcessor.PipelineActivityId); - - // Log a command stopped event - MshLog.LogCommandLifecycleEvent( - commandProcessor.Command.Context, - CommandState.Stopped, - commandProcessor.Command.MyInvocation); + EtwActivity.SetActivityId(commandProcessor.PipelineActivityId); - // Log the execution of a command (not script chunks, as they - // are not commands in and of themselves) - if (commandProcessor.CommandInfo.CommandType != CommandTypes.Script) - { - commandProcessor.CommandRuntime.PipelineProcessor.LogExecutionComplete( - commandProcessor.Command.MyInvocation, commandProcessor.CommandInfo.Name); - } + // Log a command stopped event + MshLog.LogCommandLifecycleEvent( + commandProcessor.Command.Context, + CommandState.Stopped, + commandProcessor.Command.MyInvocation); - lastCommandRuntime = commandProcessor.CommandRuntime; + // Log the execution of a command (not script chunks, as they are not commands in and of themselves). + if (commandProcessor.CommandInfo.CommandType != CommandTypes.Script) + { + LogExecutionComplete(commandProcessor.Command.MyInvocation, commandProcessor.CommandInfo.Name); } + + lastCommandRuntime = commandProcessor.CommandRuntime; } // Log the pipeline completion. - if (lastCommandRuntime != null) + if (lastCommandRuntime is not null) { // Only log the pipeline completion if this wasn't a nested pipeline, as // pipeline state in transcription is associated with the toplevel pipeline - if ((this.LocalPipeline == null) || (!this.LocalPipeline.IsNested)) + if (LocalPipeline is null || !LocalPipeline.IsNested) { lastCommandRuntime.PipelineProcessor.LogPipelineComplete(); } } // If a terminating error occurred, report it now. - if (_firstTerminatingError != null) + // This pipeline could have been stopped asynchronously, by 'Ctrl+c' manually or + // 'PowerShell.Stop' programatically. We need to check and see if that's the case. + // An example: + // - 'Start-Sleep' is running in this pipeline, and 'pipelineProcessor.Stop' gets + // called on a different thread, which sets a 'PipelineStoppedException' object + // to '_firstTerminatingError' and runs 'StopProcessing' on 'Start-Sleep'. + // - The 'StopProcessing' will cause 'Start-Sleep' to return from 'ProcessRecord' + // call, and thus the pipeline execution will move forward to run 'DoComplete' + // for the 'Start-Sleep' command and thus the code flow will reach here. + // For this given example, we need to check '_firstTerminatingError' and throw out + // the 'PipelineStoppedException' if the pipeline was indeed being stopped. + ThrowFirstErrorIfExisting(logException: true); + } + + /// + /// Clean up resources for script commands in this pipeline processor. + /// + /// + /// Exception from a 'Clean' block is not allowed to propagate up and terminate the pipeline + /// so that other 'Clean' blocks can run without being affected. Therefore, this method will + /// catch and handle all exceptions inside, and it will never throw. + /// + private void Clean() + { + if (!_executionStarted || _commands is null) { - this.LogExecutionException(_firstTerminatingError.SourceException); - _firstTerminatingError.Throw(); + // Simply return if the pipeline execution wasn't even started, or the commands of + // the pipeline have already been disposed. + return; + } + + // So far, if '_firstTerminatingError' is not null, then it must be a terminating error + // thrown from one of 'Begin/Process/End' blocks. There can be terminating error thrown + // from 'Clean' block as well, which needs to be handled in this method. + // In order to capture the subsequent first terminating error thrown from 'Clean', we + // need to forget the previous '_firstTerminatingError' value before calling 'DoClean' + // on each command processor, so we have to save the old value here and restore later. + ExceptionDispatchInfo oldFirstTerminatingError = _firstTerminatingError; + + // Suspend a stopping pipeline by setting 'IsStopping' to false and restore it afterwards. + bool oldIsStopping = ExceptionHandlingOps.SuspendStoppingPipelineImpl(LocalPipeline); + + try + { + foreach (CommandProcessorBase commandProcessor in _commands) + { + if (commandProcessor is null || !commandProcessor.HasCleanBlock) + { + continue; + } + + try + { + // Forget the terminating error we saw before, so a terminating error thrown + // from the subsequent 'Clean' block can be recorded and handled properly. + _firstTerminatingError = null; + commandProcessor.DoCleanup(); + } + catch (RuntimeException e) + { + // Retrieve and report the terminating error that was thrown in the 'Clean' block. + ExceptionDispatchInfo firstError = GetFirstError(e); + commandProcessor.ReportCleanupError(firstError.SourceException); + } + catch (Exception ex) + { + // Theoretically, only 'RuntimeException' could be thrown out, but we catch + // all and log them here just to be safe. + // Skip special flow control exceptions and log others. + if (ex is not FlowControlException && ex is not HaltCommandException) + { + MshLog.LogCommandHealthEvent(commandProcessor.Context, ex, Severity.Warning); + } + } + } + } + finally + { + _firstTerminatingError = oldFirstTerminatingError; + ExceptionHandlingOps.RestoreStoppingPipelineImpl(LocalPipeline, oldIsStopping); } } + /// + /// Clean up resources for the script commands of a steppable pipeline. + /// + /// + /// The way we handle 'Clean' blocks in 'StartStepping', 'Step', and 'DoComplete' makes sure that: + /// 1. The 'Clean' blocks get to run if any exception is thrown from the pipeline execution. + /// 2. The 'Clean' blocks get to run if the pipeline runs to the end successfully. + /// However, this is not enough for a steppable pipeline, because the function, where the steppable + /// pipeline gets used, may fail (think about a proxy function). And that may lead to the situation + /// where "no exception was thrown from the steppable pipeline" but "the steppable pipeline didn't + /// run to the end". In that case, 'Clean' won't run unless it's triggered explicitly on the steppable + /// pipeline. This method is how we will expose this functionality to 'SteppablePipeline'. + /// + internal void DoCleanup() + { + Clean(); + DisposeCommands(); + } + /// /// Implements DoComplete as a stand-alone function for completing /// the execution of a steppable pipeline. @@ -665,98 +769,79 @@ private void DoCompleteCore(CommandProcessorBase commandRequestingUpstreamComman /// The results of the execution. internal Array DoComplete() { - if (Stopping) - { - throw new PipelineStoppedException(); - } - if (!_executionStarted) { throw PSTraceSource.NewInvalidOperationException( PipelineStrings.PipelineNotStarted); } - ExceptionDispatchInfo toRethrowInfo; try { - DoCompleteCore(null); + if (Stopping) + { + throw new PipelineStoppedException(); + } - return RetrieveResults(); - } - catch (RuntimeException e) - { - // The error we want to report is the first terminating error - // which occurred during pipeline execution, regardless - // of whether other errors occurred afterward. - toRethrowInfo = _firstTerminatingError ?? ExceptionDispatchInfo.Capture(e); - this.LogExecutionException(toRethrowInfo.SourceException); - } - // NTRAID#Windows Out Of Band Releases-929020-2006/03/14-JonN - catch (System.Runtime.InteropServices.InvalidComObjectException comException) - { - // The error we want to report is the first terminating error - // which occurred during pipeline execution, regardless - // of whether other errors occurred afterward. - if (_firstTerminatingError != null) + ExceptionDispatchInfo toRethrowInfo; + try { - toRethrowInfo = _firstTerminatingError; + DoCompleteCore(null); + return RetrieveResults(); } - else + catch (RuntimeException e) { - string message = StringUtil.Format(ParserStrings.InvalidComObjectException, comException.Message); - var rte = new RuntimeException(message, comException); - rte.SetErrorId("InvalidComObjectException"); - toRethrowInfo = ExceptionDispatchInfo.Capture(rte); + toRethrowInfo = GetFirstError(e); } - this.LogExecutionException(toRethrowInfo.SourceException); + // By rethrowing the exception outside of the handler, we allow the CLR on X64/IA64 to free from the stack + // the exception records related to this exception. + + // The only reason we should get here is an exception should be rethrown. + Diagnostics.Assert(toRethrowInfo != null, "Alternate protocol path failure"); + toRethrowInfo.Throw(); + + // UNREACHABLE + return null; } finally { + Clean(); DisposeCommands(); } - - // By rethrowing the exception outside of the handler, - // we allow the CLR on X64/IA64 to free from the stack - // the exception records related to this exception. - - // The only reason we should get here is if - // an exception should be rethrown. - Diagnostics.Assert(toRethrowInfo != null, "Alternate protocol path failure"); - toRethrowInfo.Throw(); - return null; // UNREACHABLE } /// - /// This routine starts the stepping process. It is optional to - /// call this but can be useful if you want the begin clauses - /// of the pipeline to be run even when there may not be any input - /// to process as is the case for I/O redirection into a file. We - /// still want the file opened, even if there was nothing to write to it. + /// This routine starts the stepping process. It is optional to call this but can be useful + /// if you want the begin clauses of the pipeline to be run even when there may not be any + /// input to process as is the case for I/O redirection into a file. We still want the file + /// opened, even if there was nothing to write to it. /// /// True if you want to write to this pipeline. internal void StartStepping(bool expectInput) { + bool startSucceeded = false; try { Start(expectInput); + startSucceeded = true; - // If a terminating error occurred, report it now. - if (_firstTerminatingError != null) - { - _firstTerminatingError.Throw(); - } + // Check if this pipeline is being stopped asynchronously. + ThrowFirstErrorIfExisting(logException: false); } - catch (PipelineStoppedException) + catch (Exception e) { + Clean(); DisposeCommands(); - // The error we want to report is the first terminating error - // which occurred during pipeline execution, regardless - // of whether other errors occurred afterward. - if (_firstTerminatingError != null) + if (!startSucceeded && e is PipelineStoppedException) { - _firstTerminatingError.Throw(); + // When a terminating error happens during command execution, PowerShell will first save it + // to '_firstTerminatingError', and then throw a 'PipelineStoppedException' to tear down the + // pipeline. So when the caught exception here is 'PipelineStoppedException', it may not be + // the actual original terminating error. + // In this case, we want to report the first terminating error which occurred during pipeline + // execution, regardless of whether other errors occurred afterward. + ThrowFirstErrorIfExisting(logException: false); } throw; @@ -773,35 +858,35 @@ internal void Stop() // Only call StopProcessing if the pipeline is being stopped // for the first time - if (!RecordFailure(new PipelineStoppedException(), null)) + if (!RecordFailure(new PipelineStoppedException(), command: null)) + { return; + } // Retain copy of _commands in case Dispose() is called List commands = _commands; - if (commands == null) + if (commands is null) + { return; + } // Call StopProcessing() for all the commands. - for (int i = 0; i < commands.Count; i++) + foreach (CommandProcessorBase commandProcessor in commands) { - CommandProcessorBase commandProcessor = commands[i]; - if (commandProcessor == null) { throw PSTraceSource.NewInvalidOperationException(); } -#pragma warning disable 56500 + try { commandProcessor.Command.DoStopProcessing(); } catch (Exception) { - // 2004/04/26-JonN We swallow exceptions - // which occur during StopProcessing. + // We swallow exceptions which occur during StopProcessing. continue; } -#pragma warning restore 56500 } } @@ -844,43 +929,35 @@ internal void Stop() /// internal Array Step(object input) { - if (Stopping) - { - throw new PipelineStoppedException(); - } - + bool injectSucceeded = false; try { Start(true); Inject(input, enumerate: false); + injectSucceeded = true; - // If a terminating error occurred, report it now. - if (_firstTerminatingError != null) - { - _firstTerminatingError.Throw(); - } - + // Check if this pipeline is being stopped asynchronously. + ThrowFirstErrorIfExisting(logException: false); return RetrieveResults(); } - catch (PipelineStoppedException) + catch (Exception e) { + Clean(); DisposeCommands(); - // The error we want to report is the first terminating error - // which occurred during pipeline execution, regardless - // of whether other errors occurred afterward. - if (_firstTerminatingError != null) + if (!injectSucceeded && e is PipelineStoppedException) { - _firstTerminatingError.Throw(); + // When a terminating error happens during command execution, PowerShell will first save it + // to '_firstTerminatingError', and then throw a 'PipelineStoppedException' to tear down the + // pipeline. So when the caught exception here is 'PipelineStoppedException', it may not be + // the actual original terminating error. + // In this case, we want to report the first terminating error which occurred during pipeline + // execution, regardless of whether other errors occurred afterward. + ThrowFirstErrorIfExisting(logException: false); } throw; } - catch (Exception) - { - DisposeCommands(); - throw; - } } /// @@ -927,7 +1004,9 @@ private void Start(bool incomingStream) } if (_executionStarted) + { return; + } if (_commands == null || _commands.Count == 0) { @@ -936,32 +1015,18 @@ private void Start(bool incomingStream) } CommandProcessorBase firstcommandProcessor = _commands[0]; - if (firstcommandProcessor == null - || firstcommandProcessor.CommandRuntime == null) - { - throw PSTraceSource.NewInvalidOperationException( - PipelineStrings.PipelineExecuteRequiresAtLeastOneCommand); - } + ValidateCommandProcessorNotNull(firstcommandProcessor, PipelineStrings.PipelineExecuteRequiresAtLeastOneCommand); // Set the execution scope using the current scope - if (_executionScope == null) - { - _executionScope = firstcommandProcessor.Context.EngineSessionState.CurrentScope; - } + _executionScope ??= firstcommandProcessor.Context.EngineSessionState.CurrentScope; // add ExternalSuccessOutput to the last command CommandProcessorBase LastCommandProcessor = _commands[_commands.Count - 1]; - if (LastCommandProcessor == null - || LastCommandProcessor.CommandRuntime == null) - { - // "PipelineProcessor.Start(): LastCommandProcessor == null" - throw PSTraceSource.NewInvalidOperationException(); - } + ValidateCommandProcessorNotNull(LastCommandProcessor, errorMessage: null); if (ExternalSuccessOutput != null) { - LastCommandProcessor.CommandRuntime.OutputPipe.ExternalWriter - = ExternalSuccessOutput; + LastCommandProcessor.CommandRuntime.OutputPipe.ExternalWriter = ExternalSuccessOutput; } // add ExternalErrorOutput to all commands whose error @@ -981,14 +1046,11 @@ private void Start(bool incomingStream) _executionStarted = true; - // // Allocate the pipeline iteration array; note that the pipeline position for // each command starts at 1 so we need to allocate _commands.Count + 1 items. - // int[] pipelineIterationInfo = new int[_commands.Count + 1]; - // Prepare all commands from Engine's side, - // and make sure they are all valid + // Prepare all commands from Engine's side, and make sure they are all valid for (int i = 0; i < _commands.Count; i++) { CommandProcessorBase commandProcessor = _commands[i]; @@ -1000,8 +1062,6 @@ private void Start(bool incomingStream) // Generate new Activity Id for the thread Guid pipelineActivityId = EtwActivity.CreateActivityId(); - - // commandProcess.PipelineActivityId = new Activity id EtwActivity.SetActivityId(pipelineActivityId); commandProcessor.PipelineActivityId = pipelineActivityId; @@ -1011,20 +1071,14 @@ private void Start(bool incomingStream) CommandState.Started, commandProcessor.Command.MyInvocation); - // Telemetry here - // the type of command should be sent along - // commandProcessor.CommandInfo.CommandType - ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.ApplicationType, commandProcessor.Command.CommandInfo.CommandType.ToString()); #if LEGACYTELEMETRY Microsoft.PowerShell.Telemetry.Internal.TelemetryAPI.TraceExecutedCommand(commandProcessor.Command.CommandInfo, commandProcessor.Command.CommandOrigin); #endif - // Log the execution of a command (not script chunks, as they - // are not commands in and of themselves) + // Log the execution of a command (not script chunks, as they are not commands in and of themselves) if (commandProcessor.CommandInfo.CommandType != CommandTypes.Script) { - commandProcessor.CommandRuntime.PipelineProcessor.LogExecutionInfo( - commandProcessor.Command.MyInvocation, commandProcessor.CommandInfo.Name); + LogExecutionInfo(commandProcessor.Command.MyInvocation, commandProcessor.CommandInfo.Name); } InvocationInfo myInfo = commandProcessor.Command.MyInvocation; @@ -1057,8 +1111,7 @@ private void Start(bool incomingStream) } /// - /// Add ExternalErrorOutput to all commands whose error - /// output is not yet claimed. + /// Add ExternalErrorOutput to all commands whose error output is not yet claimed. /// private void SetExternalErrorOutput() { @@ -1067,14 +1120,12 @@ private void SetExternalErrorOutput() for (int i = 0; i < _commands.Count; i++) { CommandProcessorBase commandProcessor = _commands[i]; - Pipe UpstreamPipe = - commandProcessor.CommandRuntime.ErrorOutputPipe; + Pipe errorPipe = commandProcessor.CommandRuntime.ErrorOutputPipe; // check whether a cmdlet is consuming the error pipe - if (!UpstreamPipe.IsRedirected) + if (!errorPipe.IsRedirected) { - UpstreamPipe.ExternalWriter = - ExternalErrorOutput; + errorPipe.ExternalWriter = ExternalErrorOutput; } } } @@ -1085,14 +1136,9 @@ private void SetExternalErrorOutput() /// private void SetupParameterVariables() { - for (int i = 0; i < _commands.Count; i++) + foreach (CommandProcessorBase commandProcessor in _commands) { - CommandProcessorBase commandProcessor = _commands[i]; - if (commandProcessor == null || commandProcessor.CommandRuntime == null) - { - // "null command " + i - throw PSTraceSource.NewInvalidOperationException(); - } + ValidateCommandProcessorNotNull(commandProcessor, errorMessage: null); commandProcessor.CommandRuntime.SetupOutVariable(); commandProcessor.CommandRuntime.SetupErrorVariable(); @@ -1102,6 +1148,16 @@ private void SetupParameterVariables() } } + private static void ValidateCommandProcessorNotNull(CommandProcessorBase commandProcessor, string errorMessage) + { + if (commandProcessor?.CommandRuntime is null) + { + throw errorMessage is null + ? PSTraceSource.NewInvalidOperationException() + : PSTraceSource.NewInvalidOperationException(errorMessage, Array.Empty()); + } + } + /// /// Partially execute the pipeline. The output remains in /// the pipes. @@ -1131,12 +1187,7 @@ private void Inject(object input, bool enumerate) { // Add any input to the first command. CommandProcessorBase firstcommandProcessor = _commands[0]; - if (firstcommandProcessor == null - || firstcommandProcessor.CommandRuntime == null) - { - throw PSTraceSource.NewInvalidOperationException( - PipelineStrings.PipelineExecuteRequiresAtLeastOneCommand); - } + ValidateCommandProcessorNotNull(firstcommandProcessor, PipelineStrings.PipelineExecuteRequiresAtLeastOneCommand); if (input != AutomationNull.Value) { @@ -1174,27 +1225,26 @@ private void Inject(object input, bool enumerate) /// private Array RetrieveResults() { + if (_commands is null) + { + // This could happen to an expression redirection pipeline (e.g. 1 > a.txt). + // An exception may be thrown from the call to 'StartStepping' or 'Step' on the pipeline, + // which causes the pipeline commands to be disposed. + return MshCommandRuntime.StaticEmptyArray; + } + // If the error queue has been linked, it's up to the link to // deal with the output. Don't do anything here... if (!_linkedErrorOutput) { - // Retrieve any accumulated error objects from each of the pipes - // and add them to the error results hash table. - for (int i = 0; i < _commands.Count; i++) + foreach (CommandProcessorBase commandProcessor in _commands) { - CommandProcessorBase commandProcessor = _commands[i]; - if (commandProcessor == null - || commandProcessor.CommandRuntime == null) - { - // "null command or request or ErrorOutputPipe " + i - throw PSTraceSource.NewInvalidOperationException(); - } + ValidateCommandProcessorNotNull(commandProcessor, errorMessage: null); Pipe ErrorPipe = commandProcessor.CommandRuntime.ErrorOutputPipe; if (ErrorPipe.DownstreamCmdlet == null && !ErrorPipe.Empty) { - // 2003/10/02-JonN - // Do not return the same error results more than once + // Clear the error pipe if it's not empty and will not be consumed. ErrorPipe.Clear(); } } @@ -1203,26 +1253,18 @@ private Array RetrieveResults() // If the success queue has been linked, it's up to the link to // deal with the output. Don't do anything here... if (_linkedSuccessOutput) + { return MshCommandRuntime.StaticEmptyArray; + } CommandProcessorBase LastCommandProcessor = _commands[_commands.Count - 1]; - if (LastCommandProcessor == null - || LastCommandProcessor.CommandRuntime == null) - { - // "PipelineProcessor.RetrieveResults(): LastCommandProcessor == null" - throw PSTraceSource.NewInvalidOperationException(); - } + ValidateCommandProcessorNotNull(LastCommandProcessor, errorMessage: null); - Array results = - LastCommandProcessor.CommandRuntime.GetResultsAsArray(); + Array results = LastCommandProcessor.CommandRuntime.GetResultsAsArray(); - // 2003/10/02-JonN // Do not return the same results more than once LastCommandProcessor.CommandRuntime.OutputPipe.Clear(); - - if (results == null) - return MshCommandRuntime.StaticEmptyArray; - return results; + return results is null ? MshCommandRuntime.StaticEmptyArray : results; } /// @@ -1236,12 +1278,7 @@ internal void LinkPipelineSuccessOutput(Pipe pipeToUse) Dbg.Assert(pipeToUse != null, "Caller should verify pipeToUse != null"); CommandProcessorBase LastCommandProcessor = _commands[_commands.Count - 1]; - if (LastCommandProcessor == null - || LastCommandProcessor.CommandRuntime == null) - { - // "PipelineProcessor.RetrieveResults(): LastCommandProcessor == null" - throw PSTraceSource.NewInvalidOperationException(); - } + ValidateCommandProcessorNotNull(LastCommandProcessor, errorMessage: null); LastCommandProcessor.CommandRuntime.OutputPipe = pipeToUse; _linkedSuccessOutput = true; @@ -1251,15 +1288,9 @@ internal void LinkPipelineErrorOutput(Pipe pipeToUse) { Dbg.Assert(pipeToUse != null, "Caller should verify pipeToUse != null"); - for (int i = 0; i < _commands.Count; i++) + foreach (CommandProcessorBase commandProcessor in _commands) { - CommandProcessorBase commandProcessor = _commands[i]; - if (commandProcessor == null - || commandProcessor.CommandRuntime == null) - { - // "null command or request or ErrorOutputPipe " + i - throw PSTraceSource.NewInvalidOperationException(); - } + ValidateCommandProcessorNotNull(commandProcessor, errorMessage: null); if (commandProcessor.CommandRuntime.ErrorOutputPipe.DownstreamCmdlet == null) { @@ -1280,62 +1311,65 @@ internal void LinkPipelineErrorOutput(Pipe pipeToUse) private void DisposeCommands() { // Note that this is not in a lock. - // We do not make Dispose() wait until StopProcessing() - // has completed. + // We do not make Dispose() wait until StopProcessing() has completed. _stopping = true; + if (_commands is null && _redirectionPipes is null) + { + // Commands were already disposed. + return; + } + LogToEventLog(); - if (_commands != null) + if (_commands is not null) { - for (int i = 0; i < _commands.Count; i++) + foreach (CommandProcessorBase commandProcessor in _commands) { - CommandProcessorBase commandProcessor = _commands[i]; - if (commandProcessor != null) + if (commandProcessor is null) { -#pragma warning disable 56500 - // If Dispose throws an exception, record it as a - // pipeline failure and continue disposing cmdlets. - try - { - commandProcessor.CommandRuntime.RemoveVariableListsInPipe(); - commandProcessor.Dispose(); - } - // 2005/04/13-JonN: The only vaguely plausible reason - // for a failure here is an exception in Command.Dispose. - // As such, this should be covered by the overall - // exemption. - catch (Exception e) // Catch-all OK, 3rd party callout. - { - InvocationInfo myInvocation = null; - if (commandProcessor.Command != null) - myInvocation = commandProcessor.Command.MyInvocation; + continue; + } - ProviderInvocationException pie = - e as ProviderInvocationException; - if (pie != null) + // If Dispose throws an exception, record it as a pipeline failure and continue disposing cmdlets. + try + { + // Only cmdlets can have variables defined via the common parameters. + // We handle the cleanup of those variables only if we need to. + if (commandProcessor is CommandProcessor) + { + if (commandProcessor.Command is not PSScriptCmdlet) { - e = new CmdletProviderInvocationException( - pie, - myInvocation); + // For script cmdlets, the variable lists were already removed when exiting a scope. + // So we only need to take care of binary cmdlets here. + commandProcessor.CommandRuntime.RemoveVariableListsInPipe(); } - else - { - e = new CmdletInvocationException( - e, - myInvocation); - // Log a command health event + // Remove the pipeline variable if we need to. + commandProcessor.CommandRuntime.RemovePipelineVariable(); + } - MshLog.LogCommandHealthEvent( - commandProcessor.Command.Context, - e, - Severity.Warning); - } + commandProcessor.Dispose(); + } + catch (Exception e) + { + // The only vaguely plausible reason for a failure here is an exception in 'Command.Dispose'. + // As such, this should be covered by the overall exemption. + InvocationInfo myInvocation = commandProcessor.Command?.MyInvocation; + + if (e is ProviderInvocationException pie) + { + e = new CmdletProviderInvocationException(pie, myInvocation); + } + else + { + e = new CmdletInvocationException(e, myInvocation); - RecordFailure(e, commandProcessor.Command); + // Log a command health event + MshLog.LogCommandHealthEvent(commandProcessor.Command.Context, e, Severity.Warning); } -#pragma warning restore 56500 + + RecordFailure(e, commandProcessor.Command); } } } @@ -1343,25 +1377,31 @@ private void DisposeCommands() _commands = null; // Now dispose any pipes that were used for redirection... - if (_redirectionPipes != null) + if (_redirectionPipes is not null) { foreach (PipelineProcessor redirPipe in _redirectionPipes) { -#pragma warning disable 56500 + if (redirPipe is null) + { + continue; + } + + // Clean resources for script commands. + // It is possible (though very unlikely) that the call to 'Step' on the redirection pipeline failed. + // In such a case, 'Clean' would have run and the 'pipelineProcessor' would have been disposed. + // Therefore, calling 'Clean' again will simply return, because '_commands' was already set to null. + redirPipe.Clean(); + // The complicated logic of disposing the commands is taken care // of through recursion, this routine should not be getting any // exceptions... try { - if (redirPipe != null) - { - redirPipe.Dispose(); - } + redirPipe.Dispose(); } catch (Exception) { } -#pragma warning restore 56500 } } @@ -1385,11 +1425,9 @@ internal bool RecordFailure(Exception e, InternalCommand command) { _firstTerminatingError = ExceptionDispatchInfo.Capture(e); } - // 905900-2005/05/12 - // Drop5: Error Architecture: Log/trace second and subsequent RecordFailure - // Note that the pipeline could have been stopped asynchronously - // before hitting the error, therefore we check whether - // firstTerminatingError is PipelineStoppedException. + // Error Architecture: Log/trace second and subsequent RecordFailure. + // Note that the pipeline could have been stopped asynchronously before hitting the error, + // therefore we check whether '_firstTerminatingError' is 'PipelineStoppedException'. else if (_firstTerminatingError.SourceException is not PipelineStoppedException && command?.Context != null) { @@ -1408,11 +1446,10 @@ internal bool RecordFailure(Exception e, InternalCommand command) ex.GetType().Name, ex.StackTrace ); - InvalidOperationException ioe - = new InvalidOperationException(message, ex); + MshLog.LogCommandHealthEvent( command.Context, - ioe, + new InvalidOperationException(message, ex), Severity.Warning); } } diff --git a/src/System.Management.Automation/engine/remoting/client/ClientMethodExecutor.cs b/src/System.Management.Automation/engine/remoting/client/ClientMethodExecutor.cs index 8d8ba3f7e64..e779b132f4d 100644 --- a/src/System.Management.Automation/engine/remoting/client/ClientMethodExecutor.cs +++ b/src/System.Management.Automation/engine/remoting/client/ClientMethodExecutor.cs @@ -159,10 +159,7 @@ internal void Execute(PSDataCollectionStream errorStream) { try { - if (_clientHost.UI != null) - { - _clientHost.UI.WriteErrorLine(errorRecord.ToString()); - } + _clientHost.UI?.WriteErrorLine(errorRecord.ToString()); } catch (Exception) { diff --git a/src/System.Management.Automation/engine/remoting/client/ClientRemotePowerShell.cs b/src/System.Management.Automation/engine/remoting/client/ClientRemotePowerShell.cs index 4ea06ea2feb..bee284f0704 100644 --- a/src/System.Management.Automation/engine/remoting/client/ClientRemotePowerShell.cs +++ b/src/System.Management.Automation/engine/remoting/client/ClientRemotePowerShell.cs @@ -15,7 +15,7 @@ namespace System.Management.Automation.Runspaces.Internal /// PowerShell client side proxy base which handles invocation /// of powershell on a remote machine. /// - internal class ClientRemotePowerShell : IDisposable + internal sealed class ClientRemotePowerShell : IDisposable { #region Tracer @@ -166,10 +166,7 @@ internal void UnblockCollections() outputstream.Close(); errorstream.Close(); - if (inputstream != null) - { - inputstream.Close(); - } + inputstream?.Close(); } /// @@ -902,28 +899,28 @@ private void HandleRobustConnectionNotification( #endregion Private Methods - #region Protected Members - - protected ObjectStreamBase inputstream; - protected ObjectStreamBase errorstream; - protected PSInformationalBuffers informationalBuffers; - protected PowerShell shell; - protected Guid clientRunspacePoolId; - protected bool noInput; - protected PSInvocationSettings settings; - protected ObjectStreamBase outputstream; - protected string computerName; - protected ClientPowerShellDataStructureHandler dataStructureHandler; - protected bool stopCalled = false; - protected PSHost hostToUse; - protected RemoteRunspacePoolInternal runspacePool; - - protected const string WRITE_DEBUG_LINE = "WriteDebugLine"; - protected const string WRITE_VERBOSE_LINE = "WriteVerboseLine"; - protected const string WRITE_WARNING_LINE = "WriteWarningLine"; - protected const string WRITE_PROGRESS = "WriteProgress"; - - protected bool initialized = false; + #region Private Fields + + private ObjectStreamBase inputstream; + private ObjectStreamBase errorstream; + private PSInformationalBuffers informationalBuffers; + private readonly PowerShell shell; + private readonly Guid clientRunspacePoolId; + private bool noInput; + private PSInvocationSettings settings; + private ObjectStreamBase outputstream; + private readonly string computerName; + private ClientPowerShellDataStructureHandler dataStructureHandler; + private bool stopCalled = false; + private PSHost hostToUse; + private readonly RemoteRunspacePoolInternal runspacePool; + + private const string WRITE_DEBUG_LINE = "WriteDebugLine"; + private const string WRITE_VERBOSE_LINE = "WriteVerboseLine"; + private const string WRITE_WARNING_LINE = "WriteWarningLine"; + private const string WRITE_PROGRESS = "WriteProgress"; + + private bool initialized = false; /// /// This queue is for the state change events that resulted in closing the underlying /// datastructure handler. We cannot send the state back to the upper layers until @@ -933,33 +930,20 @@ private void HandleRobustConnectionNotification( private PSConnectionRetryStatus _connectionRetryStatus = PSConnectionRetryStatus.None; - #endregion Protected Members + #endregion Private Fields #region IDisposable /// - /// Public interface for dispose. + /// Release all resources. /// public void Dispose() { - Dispose(true); - - GC.SuppressFinalize(this); + // inputstream.Dispose(); + // outputstream.Dispose(); + // errorstream.Dispose(); } - /// - /// Release all resources. - /// - /// If true, release all managed resources. - protected void Dispose(bool disposing) - { - if (disposing) - { - // inputstream.Dispose(); - // outputstream.Dispose(); - // errorstream.Dispose(); - } - } #endregion IDisposable } diff --git a/src/System.Management.Automation/engine/remoting/client/Job.cs b/src/System.Management.Automation/engine/remoting/client/Job.cs index 6c7efa68f28..05ed6dfe1f1 100644 --- a/src/System.Management.Automation/engine/remoting/client/Job.cs +++ b/src/System.Management.Automation/engine/remoting/client/Job.cs @@ -633,10 +633,7 @@ public IList ChildJobs { lock (syncObject) { - if (_childJobs == null) - { - _childJobs = new List(); - } + _childJobs ??= new List(); } } @@ -1451,10 +1448,7 @@ internal void SetJobState(JobState state, Exception reason) { lock (syncObject) { - if (_finished != null) - { - _finished.Set(); - } + _finished?.Set(); } } #pragma warning restore 56500 @@ -2360,7 +2354,7 @@ private void SetStatusMessage() #region finish logic - // This variable is set to true if atleast one child job failed. + // This variable is set to true if at least one child job failed. private bool _atleastOneChildJobFailed = false; // count of number of child jobs which have finished @@ -3226,7 +3220,7 @@ protected virtual void HandleOperationComplete(object sender, OperationStateEven // no pipeline is created and no pipeline state changed event is raised. // We can wait for throttle complete, but it is raised only when all the // operations are completed and this means that status of job is not updated - // untill Operation Complete. + // until Operation Complete. ExecutionCmdletHelper helper = sender as ExecutionCmdletHelper; Dbg.Assert(helper != null, "Sender of OperationComplete has to be ExecutionCmdletHelper"); @@ -3371,13 +3365,10 @@ protected void ProcessJobFailure(ExecutionCmdletHelper helper, out Exception fai } } - if (failureException == null) - { - failureException = new RuntimeException( - PSRemotingErrorInvariants.FormatResourceString( - RemotingErrorIdStrings.RemoteRunspaceOpenUnknownState, - runspace.RunspaceStateInfo.State)); - } + failureException ??= new RuntimeException( + PSRemotingErrorInvariants.FormatResourceString( + RemotingErrorIdStrings.RemoteRunspaceOpenUnknownState, + runspace.RunspaceStateInfo.State)); failureErrorRecord = new ErrorRecord(failureException, targetObject, fullyQualifiedErrorId, ErrorCategory.OpenError, @@ -3926,7 +3917,7 @@ public override void SetBreakpoints(IEnumerable breakpoints, int? ru /// /// Id of the breakpoint you want. /// The runspace id of the runspace you want to interact with. A null value will use the current runspace. - /// A a breakpoint with the specified id. + /// A breakpoint with the specified id. public override Breakpoint GetBreakpoint(int id, int? runspaceId) => _wrappedDebugger.GetBreakpoint(id, runspaceId); @@ -4080,10 +4071,7 @@ public override void SetDebuggerStepMode(bool enabled) internal void CheckStateAndRaiseStopEvent() { RemoteDebugger remoteDebugger = _wrappedDebugger as RemoteDebugger; - if (remoteDebugger != null) - { - remoteDebugger.CheckStateAndRaiseStopEvent(); - } + remoteDebugger?.CheckStateAndRaiseStopEvent(); } /// @@ -4131,13 +4119,7 @@ private Pipeline DrainAndBlockRemoteOutput() return null; } - private static void RestoreRemoteOutput(Pipeline runningCmd) - { - if (runningCmd != null) - { - runningCmd.ResumeIncomingData(); - } - } + private static void RestoreRemoteOutput(Pipeline runningCmd) => runningCmd?.ResumeIncomingData(); private void HandleBreakpointUpdated(object sender, BreakpointUpdatedEventArgs e) { diff --git a/src/System.Management.Automation/engine/remoting/client/Job2.cs b/src/System.Management.Automation/engine/remoting/client/Job2.cs index b89c9433697..d5f122159ed 100644 --- a/src/System.Management.Automation/engine/remoting/client/Job2.cs +++ b/src/System.Management.Automation/engine/remoting/client/Job2.cs @@ -93,8 +93,7 @@ public List StartParameters { lock (_syncobject) { - if (_parameters == null) - _parameters = new List(); + _parameters ??= new List(); } } @@ -506,7 +505,7 @@ public sealed class ContainerParentJob : Job2 private const int DisposedTrue = 1; private const int DisposedFalse = 0; - // This variable is set to true if atleast one child job failed. + // This variable is set to true if at least one child job failed. // count of number of child jobs which have finished private int _finishedChildJobsCount = 0; @@ -2022,11 +2021,8 @@ protected override void Dispose(bool disposing) job.Dispose(); } - if (_jobRunning != null) - _jobRunning.Dispose(); - - if (_jobSuspendedOrAborted != null) - _jobSuspendedOrAborted.Dispose(); + _jobRunning?.Dispose(); + _jobSuspendedOrAborted?.Dispose(); } finally { diff --git a/src/System.Management.Automation/engine/remoting/client/RemoteRunspacePoolInternal.cs b/src/System.Management.Automation/engine/remoting/client/RemoteRunspacePoolInternal.cs index 94584c613f5..6d19c053e90 100644 --- a/src/System.Management.Automation/engine/remoting/client/RemoteRunspacePoolInternal.cs +++ b/src/System.Management.Automation/engine/remoting/client/RemoteRunspacePoolInternal.cs @@ -81,7 +81,7 @@ internal RemoteRunspacePoolInternal(int minRunspaces, minPoolSz.ToString(CultureInfo.InvariantCulture), maxPoolSz.ToString(CultureInfo.InvariantCulture)); - _connectionInfo = connectionInfo.InternalCopy(); + _connectionInfo = connectionInfo.Clone(); this.host = host; ApplicationArguments = applicationArguments; @@ -128,7 +128,7 @@ internal RemoteRunspacePoolInternal(Guid instanceId, string name, bool isDisconn if (connectionInfo is WSManConnectionInfo) { - _connectionInfo = connectionInfo.InternalCopy(); + _connectionInfo = connectionInfo.Clone(); } else { @@ -1826,20 +1826,14 @@ private void ResetDisconnectedOnExpiresOn() { // Reset DisconnectedOn/ExpiresOn WSManConnectionInfo wsManConnectionInfo = _connectionInfo as WSManConnectionInfo; - if (wsManConnectionInfo != null) - { - wsManConnectionInfo.NullDisconnectedExpiresOn(); - } + wsManConnectionInfo?.NullDisconnectedExpiresOn(); } private void UpdateDisconnectedExpiresOn() { // Set DisconnectedOn/ExpiresOn for disconnected session. WSManConnectionInfo wsManConnectionInfo = _connectionInfo as WSManConnectionInfo; - if (wsManConnectionInfo != null) - { - wsManConnectionInfo.SetDisconnectedExpiresOnToNow(); - } + wsManConnectionInfo?.SetDisconnectedExpiresOnToNow(); } /// diff --git a/src/System.Management.Automation/engine/remoting/client/RemotingProtocol2.cs b/src/System.Management.Automation/engine/remoting/client/RemotingProtocol2.cs index 8b8908b6e61..ecc3d6cfd9f 100644 --- a/src/System.Management.Automation/engine/remoting/client/RemotingProtocol2.cs +++ b/src/System.Management.Automation/engine/remoting/client/RemotingProtocol2.cs @@ -275,10 +275,7 @@ internal void DispatchMessageToPowerShell(RemoteDataObject rcvdData) // if a data structure handler does not exist it means // the association has been removed - // discard messages - if (dsHandler != null) - { - dsHandler.ProcessReceivedData(rcvdData); - } + dsHandler?.ProcessReceivedData(rcvdData); } /// @@ -813,7 +810,7 @@ private void HandleReadyForDisconnect(object sender, EventArgs args) // what thread this callback is made from. If it was made from a transport // callback event then a deadlock may occur when DisconnectAsync is called on // that same thread. - ThreadPool.QueueUserWorkItem(new WaitCallback(StartDisconnectAsync), RemoteSession); + ThreadPool.QueueUserWorkItem(new WaitCallback(StartDisconnectAsync)); } } } @@ -821,10 +818,18 @@ private void HandleReadyForDisconnect(object sender, EventArgs args) /// /// WaitCallback method to start an asynchronous disconnect. /// - /// - private void StartDisconnectAsync(object remoteSession) + /// + private void StartDisconnectAsync(object state) { - ((ClientRemoteSession)remoteSession).DisconnectAsync(); + var remoteSession = RemoteSession; + try + { + remoteSession?.DisconnectAsync(); + } + catch + { + // remoteSession may have already been disposed resulting in unexpected exceptions. + } } /// diff --git a/src/System.Management.Automation/engine/remoting/client/RunspaceRef.cs b/src/System.Management.Automation/engine/remoting/client/RunspaceRef.cs index dfd1cd3bf7f..b3bfbe9a0d9 100644 --- a/src/System.Management.Automation/engine/remoting/client/RunspaceRef.cs +++ b/src/System.Management.Automation/engine/remoting/client/RunspaceRef.cs @@ -208,12 +208,9 @@ internal Pipeline CreatePipeline(string line, bool addToHistory, bool useNestedP } // If that didn't work out fall-back to the traditional approach. - if (pipeline == null) - { - pipeline = useNestedPipelines ? - _runspaceRef.Value.CreateNestedPipeline(line, addToHistory) : - _runspaceRef.Value.CreatePipeline(line, addToHistory); - } + pipeline ??= useNestedPipelines ? + _runspaceRef.Value.CreateNestedPipeline(line, addToHistory) : + _runspaceRef.Value.CreatePipeline(line, addToHistory); // Add robust connection callback if this is a pushed runspace. RemotePipeline remotePipeline = pipeline as RemotePipeline; diff --git a/src/System.Management.Automation/engine/remoting/client/ThrottlingJob.cs b/src/System.Management.Automation/engine/remoting/client/ThrottlingJob.cs index db2f5403dfd..000e544fd57 100644 --- a/src/System.Management.Automation/engine/remoting/client/ThrottlingJob.cs +++ b/src/System.Management.Automation/engine/remoting/client/ThrottlingJob.cs @@ -53,11 +53,7 @@ protected override void Dispose(bool disposing) childJob.Dispose(); } - if (_jobResultsThrottlingSemaphore != null) - { - _jobResultsThrottlingSemaphore.Dispose(); - } - + _jobResultsThrottlingSemaphore?.Dispose(); _cancellationTokenSource.Dispose(); } } @@ -541,10 +537,7 @@ private void StartChildJobIfPossible() } while (false); } - if (readyToRunChildJob != null) - { - readyToRunChildJob.StartJob(); - } + readyToRunChildJob?.StartJob(); } private void EnqueueReadyToRunChildJob(StartableJob childJob) @@ -1227,10 +1220,7 @@ public static void ForwardAllResultsToCmdlet(ThrottlingJob throttlingJob, Cmdlet } finally { - if (cancellationTokenRegistration != null) - { - cancellationTokenRegistration.Dispose(); - } + cancellationTokenRegistration?.Dispose(); } } finally diff --git a/src/System.Management.Automation/engine/remoting/client/clientremotesessionprotocolstatemachine.cs b/src/System.Management.Automation/engine/remoting/client/clientremotesessionprotocolstatemachine.cs index d035ce6e820..da63b846543 100644 --- a/src/System.Management.Automation/engine/remoting/client/clientremotesessionprotocolstatemachine.cs +++ b/src/System.Management.Automation/engine/remoting/client/clientremotesessionprotocolstatemachine.cs @@ -254,10 +254,7 @@ private void SetStateHandler(object sender, RemoteSessionStateMachineEventArgs e if (_state == RemoteSessionState.EstablishedAndKeySent) { Timer tmp = Interlocked.Exchange(ref _keyExchangeTimer, null); - if (tmp != null) - { - tmp.Dispose(); - } + tmp?.Dispose(); _keyExchanged = true; SetState(RemoteSessionState.Established, eventArgs.Reason); @@ -339,10 +336,7 @@ private void HandleKeyExchangeTimeout(object sender) Dbg.Assert(_state == RemoteSessionState.EstablishedAndKeySent, "timeout should only happen when waiting for a key"); Timer tmp = Interlocked.Exchange(ref _keyExchangeTimer, null); - if (tmp != null) - { - tmp.Dispose(); - } + tmp?.Dispose(); PSRemotingDataStructureException exception = new PSRemotingDataStructureException(RemotingErrorIdStrings.ClientKeyExchangeFailed); @@ -456,7 +450,7 @@ internal ClientRemoteSessionDSHandlerStateMachine() _stateMachineHandle[(int)RemoteSessionState.EstablishedAndKeyRequested, (int)RemoteSessionEvent.KeySendFailed] += SetStateToClosedHandler; // TODO: All these are potential unexpected state transitions.. should have a way to track these calls.. - // should atleast put a dbg assert in this handler + // should at least put a dbg assert in this handler for (int i = 0; i < _stateMachineHandle.GetLength(0); i++) { for (int j = 0; j < _stateMachineHandle.GetLength(1); j++) diff --git a/src/System.Management.Automation/engine/remoting/client/remotepipeline.cs b/src/System.Management.Automation/engine/remoting/client/remotepipeline.cs index 164e9f11f39..67e4b7153f3 100644 --- a/src/System.Management.Automation/engine/remoting/client/remotepipeline.cs +++ b/src/System.Management.Automation/engine/remoting/client/remotepipeline.cs @@ -34,7 +34,7 @@ internal class RemotePipeline : Pipeline private readonly ConnectCommandInfo _connectCmdInfo = null; /// - /// This is queue of all the state change event which have occured for + /// This is queue of all the state change event which have occurred for /// this pipeline. RaisePipelineStateEvents raises event for each /// item in this queue. We don't raise the event with in SetPipelineState /// because often SetPipelineState is called with in a lock. diff --git a/src/System.Management.Automation/engine/remoting/client/remoterunspace.cs b/src/System.Management.Automation/engine/remoting/client/remoterunspace.cs index 7ddd657872e..ca6d8f7d517 100644 --- a/src/System.Management.Automation/engine/remoting/client/remoterunspace.cs +++ b/src/System.Management.Automation/engine/remoting/client/remoterunspace.cs @@ -48,7 +48,7 @@ internal class RemoteRunspace : Runspace, IDisposable private long _currentLocalPipelineId = 0; /// - /// This is queue of all the state change event which have occured for + /// This is queue of all the state change event which have occurred for /// this runspace. RaiseRunspaceStateEvents raises event for each /// item in this queue. We don't raise events from with SetRunspaceState /// because SetRunspaceState is often called from with in the a lock. @@ -137,8 +137,8 @@ internal RemoteRunspace(TypeTable typeTable, RunspaceConnectionInfo connectionIn PSTask.CreateRunspace, PSKeyword.UseAlwaysOperational, InstanceId.ToString()); - _connectionInfo = connectionInfo.InternalCopy(); - OriginalConnectionInfo = connectionInfo.InternalCopy(); + _connectionInfo = connectionInfo.Clone(); + OriginalConnectionInfo = connectionInfo.Clone(); RunspacePool = new RunspacePool(1, 1, typeTable, host, applicationArguments, connectionInfo, name); @@ -170,7 +170,7 @@ internal RemoteRunspace(RunspacePool runspacePool) RunspacePool.RemoteRunspacePoolInternal.SetMinRunspaces(1); RunspacePool.RemoteRunspacePoolInternal.SetMaxRunspaces(1); - _connectionInfo = runspacePool.ConnectionInfo.InternalCopy(); + _connectionInfo = runspacePool.ConnectionInfo.Clone(); // Update runspace DisconnectedOn and ExpiresOn property from WSManConnectionInfo UpdateDisconnectExpiresOn(); @@ -221,11 +221,7 @@ public override InitialSessionState InitialSessionState { get { -#pragma warning disable 56503 - throw PSTraceSource.NewNotImplementedException(); - -#pragma warning restore 56503 } } @@ -236,11 +232,7 @@ public override JobManager JobManager { get { -#pragma warning disable 56503 - throw PSTraceSource.NewNotImplementedException(); - -#pragma warning restore 56503 } } @@ -642,11 +634,8 @@ protected override void Dispose(bool disposing) // } - if (_remoteDebugger != null) - { - // Release RunspacePool event forwarding handlers. - _remoteDebugger.Dispose(); - } + // Release RunspacePool event forwarding handlers. + _remoteDebugger?.Dispose(); try { @@ -946,6 +935,11 @@ public override RunspaceCapability GetCapabilities() returnCaps |= RunspaceCapability.SupportsDisconnect; } + if (_connectionInfo is WSManConnectionInfo) + { + return returnCaps; + } + if (_connectionInfo is NamedPipeConnectionInfo) { returnCaps |= RunspaceCapability.NamedPipeTransport; @@ -958,16 +952,20 @@ public override RunspaceCapability GetCapabilities() { returnCaps |= RunspaceCapability.SSHTransport; } - else + else if (_connectionInfo is ContainerConnectionInfo containerConnectionInfo) { - ContainerConnectionInfo containerConnectionInfo = _connectionInfo as ContainerConnectionInfo; - if ((containerConnectionInfo != null) && (containerConnectionInfo.ContainerProc.RuntimeId == Guid.Empty)) { returnCaps |= RunspaceCapability.NamedPipeTransport; } } + else + { + // Unknown connection info type means a custom connection/transport, which at + // minimum supports remote runspace capability starting from PowerShell v7.x. + returnCaps |= RunspaceCapability.CustomTransport; + } return returnCaps; } @@ -1731,10 +1729,7 @@ internal void AbortOpen() System.Management.Automation.Remoting.Client.NamedPipeClientSessionTransportManager transportManager = RunspacePool.RemoteRunspacePoolInternal.DataStructureHandler.TransportManager as System.Management.Automation.Remoting.Client.NamedPipeClientSessionTransportManager; - if (transportManager != null) - { - transportManager.AbortConnect(); - } + transportManager?.AbortConnect(); } #endregion Internal Methods @@ -2910,10 +2905,7 @@ private T InvokeRemoteBreakpointFunction(string functionName, Dictionary /// Static variable which is incremented to generate id. @@ -302,7 +303,8 @@ internal PSSession(RemoteRunspace remoteRunspace) break; default: - Dbg.Assert(false, "Invalid Runspace"); + // Default for custom connection and transports. + ComputerType = TargetMachineType.RemoteMachine; break; } } @@ -338,7 +340,7 @@ private string GetTransportName() return "VMBus"; default: - return "Unknown"; + return string.IsNullOrEmpty(_transportName) ? "Custom" : _transportName; } } @@ -359,6 +361,34 @@ private static string GetDisplayShellName(string shell) #region Static Methods + /// + /// Creates a PSSession object from the provided remote runspace object. + /// If psCmdlet argument is non-null, then the new PSSession object is added to the + /// session runspace repository (Get-PSSession). + /// + /// Runspace for the new PSSession. + /// Optional transport name. + /// Optional cmdlet associated with the PSSession creation. + public static PSSession Create( + Runspace runspace, + string transportName, + PSCmdlet psCmdlet) + { + if (!(runspace is RemoteRunspace remoteRunspace)) + { + throw new PSArgumentException(RemotingErrorIdStrings.InvalidPSSessionArgument); + } + + var psSession = new PSSession(remoteRunspace) + { + _transportName = transportName + }; + + psCmdlet?.RunspaceRepository.Add(psSession); + + return psSession; + } + /// /// Generates a unique runspace id. /// diff --git a/src/System.Management.Automation/engine/remoting/commands/ConnectPSSession.cs b/src/System.Management.Automation/engine/remoting/commands/ConnectPSSession.cs index 8dfdacd1226..b84b36e727e 100644 --- a/src/System.Management.Automation/engine/remoting/commands/ConnectPSSession.cs +++ b/src/System.Management.Automation/engine/remoting/commands/ConnectPSSession.cs @@ -569,10 +569,7 @@ internal override void StartOperation() internal override void StopOperation() { - if (_queryRunspaces != null) - { - _queryRunspaces.StopAllOperations(); - } + _queryRunspaces?.StopAllOperations(); _session.Runspace.StateChanged -= StateCallBackHandler; SendStopComplete(); diff --git a/src/System.Management.Automation/engine/remoting/commands/CustomShellCommands.cs b/src/System.Management.Automation/engine/remoting/commands/CustomShellCommands.cs index 19c093e3687..76601ff6dfb 100644 --- a/src/System.Management.Automation/engine/remoting/commands/CustomShellCommands.cs +++ b/src/System.Management.Automation/engine/remoting/commands/CustomShellCommands.cs @@ -2320,13 +2320,8 @@ public Version PSVersion set { - RemotingCommandUtil.CheckPSVersion(value); - - // Check if specified version of PowerShell is installed - RemotingCommandUtil.CheckIfPowerShellVersionIsInstalled(value); - - psVersion = value; - isPSVersionSpecified = true; + // PowerShell 7 remoting endpoints do not support PSVersion. + throw new PSNotSupportedException(RemotingErrorIdStrings.PowerShellVersionNotSupported); } } diff --git a/src/System.Management.Automation/engine/remoting/commands/DebugJob.cs b/src/System.Management.Automation/engine/remoting/commands/DebugJob.cs index 8e5732304e9..7494f902fb7 100644 --- a/src/System.Management.Automation/engine/remoting/commands/DebugJob.cs +++ b/src/System.Management.Automation/engine/remoting/commands/DebugJob.cs @@ -203,10 +203,7 @@ protected override void StopProcessing() // Unblock the data collection. PSDataCollection debugCollection = _debugCollection; - if (debugCollection != null) - { - debugCollection.Complete(); - } + debugCollection?.Complete(); } #endregion @@ -260,10 +257,7 @@ private void WaitAndReceiveJobOutput() // or this command is cancelled. foreach (var streamItem in _debugCollection) { - if (streamItem != null) - { - streamItem.WriteStreamObject(this); - } + streamItem?.WriteStreamObject(this); } } catch (Exception) diff --git a/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs b/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs index 756250eb8d7..b6906bc7008 100644 --- a/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs @@ -212,10 +212,7 @@ protected override void EndProcessing() protected override void StopProcessing() { RemoteRunspace connectingRunspace = _connectingRemoteRunspace; - if (connectingRunspace != null) - { - connectingRunspace.AbortOpen(); - } + connectingRunspace?.AbortOpen(); } #endregion @@ -318,22 +315,19 @@ private static void PrepareRunspace(Runspace runspace) private Process GetProcessById(int procId) { - try - { - return Process.GetProcessById(procId); - } - catch (System.ArgumentException) + var process = PSHostProcessUtils.GetProcessById(procId); + if (process is null) { ThrowTerminatingError( - new ErrorRecord( - new PSArgumentException(StringUtil.Format(RemotingErrorIdStrings.EnterPSHostProcessNoProcessFoundWithId, procId)), - "EnterPSHostProcessNoProcessFoundWithId", - ErrorCategory.InvalidArgument, - this) - ); - - return null; + new ErrorRecord( + new PSArgumentException(StringUtil.Format(RemotingErrorIdStrings.EnterPSHostProcessNoProcessFoundWithId, procId)), + "EnterPSHostProcessNoProcessFoundWithId", + ErrorCategory.InvalidArgument, + this) + ); } + + return process; } private Process GetProcessByHostProcessInfo(PSHostProcessInfo hostProcessInfo) @@ -403,7 +397,7 @@ private void VerifyProcess(Process process) { ThrowTerminatingError( new ErrorRecord( - new PSInvalidOperationException(StringUtil.Format(RemotingErrorIdStrings.EnterPSHostProcessNoPowerShell, Process.ProcessName)), + new PSInvalidOperationException(StringUtil.Format(RemotingErrorIdStrings.EnterPSHostProcessNoPowerShell, Process.Id)), "EnterPSHostProcessNoPowerShell", ErrorCategory.InvalidOperation, this) @@ -599,9 +593,22 @@ private static int[] GetProcIdsFromNames(string[] names) WildcardPattern namePattern = WildcardPattern.Get(name, WildcardOptions.IgnoreCase); foreach (var proc in processes) { - if (namePattern.IsMatch(proc.ProcessName)) + // Skip processes that have already terminated. + if (proc.HasExited) + { + continue; + } + + try { - returnIds.Add(proc.Id); + if (namePattern.IsMatch(proc.ProcessName)) + { + returnIds.Add(proc.Id); + } + } + catch (InvalidOperationException) + { + // Ignore if process has exited in the mean time. } } } @@ -681,10 +688,9 @@ internal static IReadOnlyCollection GetAppDomainNamesFromProc string pName = namedPipe.Substring(pNameIndex + 1); Process process = null; - try { - process = System.Diagnostics.Process.GetProcessById(id); + process = PSHostProcessUtils.GetProcessById(id); } catch (Exception) { @@ -704,10 +710,20 @@ internal static IReadOnlyCollection GetAppDomainNamesFromProc // best effort to cleanup } } - else if (process.ProcessName.Equals(pName, StringComparison.Ordinal)) + else { - // only add if the process name matches - procAppDomainInfo.Add(new PSHostProcessInfo(pName, id, appDomainName, namedPipe)); + try + { + if (process.ProcessName.Equals(pName, StringComparison.Ordinal)) + { + // only add if the process name matches + procAppDomainInfo.Add(new PSHostProcessInfo(pName, id, appDomainName, namedPipe)); + } + } + catch (InvalidOperationException) + { + // Ignore if process has exited in the mean time. + } } } } @@ -796,8 +812,8 @@ internal PSHostProcessInfo( MainWindowTitle = string.Empty; try { - var proc = Process.GetProcessById(processId); - MainWindowTitle = proc.MainWindowTitle ?? string.Empty; + var process = PSHostProcessUtils.GetProcessById(processId); + MainWindowTitle = process?.MainWindowTitle ?? string.Empty; } catch (ArgumentException) { @@ -831,4 +847,30 @@ public string GetPipeNameFilePath() } #endregion + + #region PSHostProcessUtils + + internal static class PSHostProcessUtils + { + /// + /// Return a System.Diagnostics.Process object by process Id, + /// or null if not found or process has exited. + /// + /// Process of Id to find. + /// Process object or null. + public static Process GetProcessById(int procId) + { + try + { + var process = Process.GetProcessById(procId); + return process.HasExited ? null : process; + } + catch (System.ArgumentException) + { + return null; + } + } + } + + #endregion } diff --git a/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs b/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs index c86f7903a1a..a2a2397cb7a 100644 --- a/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs @@ -792,6 +792,25 @@ public override Hashtable[] SSHConnection set; } + /// + /// Hashtable containing options to be passed to OpenSSH. + /// + [Parameter(ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)] + [Parameter(ParameterSetName = InvokeCommandCommand.FilePathSSHHostParameterSet)] + [ValidateNotNullOrEmpty] + public override Hashtable Options + { + get + { + return base.Options; + } + + set + { + base.Options = value; + } + } + #endregion #region Remote Debug Parameters @@ -1444,10 +1463,7 @@ private void HandleRunspaceDebugStop(object sender, StartRunspaceDebugProcessing operation.RunspaceDebugStop -= HandleRunspaceDebugStop; var hostDebugger = GetHostDebugger(); - if (hostDebugger != null) - { - hostDebugger.QueueRunspaceForDebug(args.Runspace); - } + hostDebugger?.QueueRunspaceForDebug(args.Runspace); } private void HandleJobStateChanged(object sender, JobStateEventArgs e) @@ -1464,10 +1480,7 @@ private void HandleJobStateChanged(object sender, JobStateEventArgs e) // Signal that this job has been disconnected, or has ended. lock (_jobSyncObject) { - if (_disconnectComplete != null) - { - _disconnectComplete.Set(); - } + _disconnectComplete?.Set(); } } } @@ -2071,11 +2084,8 @@ private void Dispose(bool disposing) if (!_asjob) { - if (_job != null) - { - // job will be null in the "InProcess" case - _job.Dispose(); - } + // job will be null in the "InProcess" case + _job?.Dispose(); _throttleManager.ThrottleComplete -= HandleThrottleComplete; _throttleManager.Dispose(); diff --git a/src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs b/src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs index 658876c1d8d..e5aa6bef798 100644 --- a/src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs +++ b/src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs @@ -15,7 +15,6 @@ namespace Microsoft.PowerShell.Commands { -#if !UNIX /// /// New-PSSessionConfigurationFile command implementation /// @@ -1126,7 +1125,6 @@ private bool ShouldGenerateConfigurationSnippet(string parameterName) #endregion } -#endif /// /// New-PSRoleCapabilityFile command implementation diff --git a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs index d4f5ca427ab..23e86e9669a 100644 --- a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs +++ b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs @@ -287,6 +287,7 @@ internal struct SSHConnection public int Port; public string Subsystem; public int ConnectingTimeout; + public Hashtable Options; } /// @@ -805,6 +806,13 @@ public virtual Hashtable[] SSHConnection set; } + /// + /// Gets or sets the Hashtable containing options to be passed to OpenSSH. + /// + [Parameter(ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)] + [ValidateNotNullOrEmpty] + public virtual Hashtable Options { get; set; } + #endregion #endregion Properties @@ -866,6 +874,7 @@ internal static void ValidateSpecifiedAuthentication(PSCredential credential, st private const string PortParameter = "Port"; private const string SubsystemParameter = "Subsystem"; private const string ConnectingTimeoutParameter = "ConnectingTimeout"; + private const string OptionsParameter = "Options"; #endregion @@ -969,6 +978,10 @@ internal SSHConnection[] ParseSSHConnectionHashTable() { connectionInfo.ConnectingTimeout = GetSSHConnectionIntParameter(item[paramName]); } + else if (paramName.Equals(OptionsParameter, StringComparison.OrdinalIgnoreCase)) + { + connectionInfo.Options = item[paramName] as Hashtable; + } else { throw new PSArgumentException( @@ -1462,7 +1475,7 @@ protected void CreateHelpersForSpecifiedSSHComputerNames() { ParseSshHostName(computerName, out string host, out string userName, out int port); - var sshConnectionInfo = new SSHConnectionInfo(userName, host, KeyFilePath, port, Subsystem, ConnectingTimeout); + var sshConnectionInfo = new SSHConnectionInfo(userName, host, KeyFilePath, port, Subsystem, ConnectingTimeout, Options); var typeTable = TypeTable.LoadDefaultTypeFiles(); var remoteRunspace = RunspaceFactory.CreateRunspace(sshConnectionInfo, Host, typeTable) as RemoteRunspace; var pipeline = CreatePipeline(remoteRunspace); @@ -1947,11 +1960,7 @@ private static string GetRemoteServerPsVersion(RemoteRunspace remoteRunspace) /// /// Adds forwarded events to the local queue. /// - internal void OnRunspacePSEventReceived(object sender, PSEventArgs e) - { - if (this.Events != null) - this.Events.AddForwardedEvent(e); - } + internal void OnRunspacePSEventReceived(object sender, PSEventArgs e) => this.Events?.AddForwardedEvent(e); #endregion Private Methods @@ -2374,7 +2383,8 @@ private string GetConvertedScript(out List newParameterNames, out List + /// Gets or sets the Hashtable containing options to be passed to OpenSSH. + /// + [Parameter(ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)] + [ValidateNotNullOrEmpty] + public override Hashtable Options + { + get + { + return base.Options; + } + + set + { + base.Options = value; + } + } + #endregion /// @@ -247,7 +265,7 @@ protected override void ProcessRecord() } // for the console host and Graphical PowerShell host - // we want to skip pushing into the the runspace if + // we want to skip pushing into the runspace if // the host is in a nested prompt System.Management.Automation.Internal.Host.InternalHost chost = this.Host as System.Management.Automation.Internal.Host.InternalHost; @@ -1262,7 +1280,7 @@ private RemoteRunspace GetRunspaceForContainerSession() private RemoteRunspace GetRunspaceForSSHSession() { ParseSshHostName(HostName, out string host, out string userName, out int port); - var sshConnectionInfo = new SSHConnectionInfo(userName, host, KeyFilePath, port, Subsystem, ConnectingTimeout); + var sshConnectionInfo = new SSHConnectionInfo(userName, host, KeyFilePath, port, Subsystem, ConnectingTimeout, Options); var typeTable = TypeTable.LoadDefaultTypeFiles(); // Use the class _tempRunspace field while the runspace is being opened so that StopProcessing can be handled at that time. diff --git a/src/System.Management.Automation/engine/remoting/commands/ReceiveJob.cs b/src/System.Management.Automation/engine/remoting/commands/ReceiveJob.cs index 97585f1de1b..a62cef7d5d4 100644 --- a/src/System.Management.Automation/engine/remoting/commands/ReceiveJob.cs +++ b/src/System.Management.Automation/engine/remoting/commands/ReceiveJob.cs @@ -823,10 +823,7 @@ private void WriteJobResults(Job job) { if (v == null) continue; MshCommandRuntime mshCommandRuntime = CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteVerbose(v, true); - } + mshCommandRuntime?.WriteVerbose(v, true); } Collection debugRecords = ReadAll(job.Debug); @@ -835,10 +832,7 @@ private void WriteJobResults(Job job) { if (d == null) continue; MshCommandRuntime mshCommandRuntime = CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteDebug(d, true); - } + mshCommandRuntime?.WriteDebug(d, true); } Collection warningRecords = ReadAll(job.Warning); @@ -847,10 +841,7 @@ private void WriteJobResults(Job job) { if (w == null) continue; MshCommandRuntime mshCommandRuntime = CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteWarning(w, true); - } + mshCommandRuntime?.WriteWarning(w, true); } Collection progressRecords = ReadAll(job.Progress); @@ -859,10 +850,7 @@ private void WriteJobResults(Job job) { if (p == null) continue; MshCommandRuntime mshCommandRuntime = CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteProgress(p, true); - } + mshCommandRuntime?.WriteProgress(p, true); } Collection informationRecords = ReadAll(job.Information); @@ -871,10 +859,7 @@ private void WriteJobResults(Job job) { if (p == null) continue; MshCommandRuntime mshCommandRuntime = CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteInformation(p, true); - } + mshCommandRuntime?.WriteInformation(p, true); } } @@ -1065,10 +1050,7 @@ private void AggregateResultsFromJob(Job job) { lock (_syncObject) { - if (_outputProcessingNotification == null) - { - _outputProcessingNotification = new OutputProcessingState(); - } + _outputProcessingNotification ??= new OutputProcessingState(); } } diff --git a/src/System.Management.Automation/engine/remoting/commands/ReceivePSSession.cs b/src/System.Management.Automation/engine/remoting/commands/ReceivePSSession.cs index 98d2afa3b5f..9704216e053 100644 --- a/src/System.Management.Automation/engine/remoting/commands/ReceivePSSession.cs +++ b/src/System.Management.Automation/engine/remoting/commands/ReceivePSSession.cs @@ -36,7 +36,7 @@ namespace Microsoft.PowerShell.Commands /// /// The user can specify how command output data is returned by using the public /// OutTarget enumeration (Host, Job). - /// The default actions of this cmdlet is to always direct ouput to host unless + /// The default actions of this cmdlet is to always direct output to host unless /// a job object already exists on the client that is associated with the running /// command. In this case the existing job object is connected to the running /// command and returned. @@ -404,15 +404,8 @@ protected override void StopProcessing() tmpJob = _job; } - if (tmpPipeline != null) - { - tmpPipeline.StopAsync(); - } - - if (tmpJob != null) - { - tmpJob.StopJob(); - } + tmpPipeline?.StopAsync(); + tmpJob?.StopJob(); } #endregion @@ -818,19 +811,13 @@ private void DisconnectAndStopRunningCmds(RemoteRunspace remoteRunspace) remoteRunspace.Disconnect(); - if (stopPipelineReceive != null) + try { - try - { - stopPipelineReceive.Set(); - } - catch (ObjectDisposedException) { } + stopPipelineReceive?.Set(); } + catch (ObjectDisposedException) { } - if (job != null) - { - job.StopJob(); - } + job?.StopJob(); } } @@ -876,10 +863,7 @@ private void ConnectSessionToHost(PSSession session, PSRemotingJob job = null) foreach (var result in childJob.ReadAll()) { - if (result != null) - { - result.WriteStreamObject(this); - } + result?.WriteStreamObject(this); } if (index == 0) @@ -1340,7 +1324,7 @@ public enum OutTarget Host = 1, /// - /// Asynchronous mode. Receive-PSSession ouput data goes to returned job object. + /// Asynchronous mode. Receive-PSSession output data goes to returned job object. /// Job = 2 } diff --git a/src/System.Management.Automation/engine/remoting/commands/StartJob.cs b/src/System.Management.Automation/engine/remoting/commands/StartJob.cs index 90e10bb8f44..1436626e008 100644 --- a/src/System.Management.Automation/engine/remoting/commands/StartJob.cs +++ b/src/System.Management.Automation/engine/remoting/commands/StartJob.cs @@ -512,10 +512,12 @@ public virtual Version PSVersion set { - RemotingCommandUtil.CheckPSVersion(value); - - // Check if specified version of PowerShell is installed - RemotingCommandUtil.CheckIfPowerShellVersionIsInstalled(value); + // PSVersion value can only be 5.1 for Start-Job. + if (!(value.Major == 5 && value.Minor == 1)) + { + throw new ArgumentException( + StringUtil.Format(RemotingErrorIdStrings.PSVersionParameterOutOfRange, value, "PSVersion")); + } _psVersion = value; } @@ -599,7 +601,7 @@ protected override void BeginProcessing() ThrowTerminatingError(errorRecord); } - if (WorkingDirectory != null && !Directory.Exists(WorkingDirectory)) + if (WorkingDirectory != null && !InvokeProvider.Item.IsContainer(WorkingDirectory)) { string message = StringUtil.Format(RemotingErrorIdStrings.StartJobWorkingDirectoryNotFound, WorkingDirectory); var errorRecord = new ErrorRecord( diff --git a/src/System.Management.Automation/engine/remoting/commands/getrunspacecommand.cs b/src/System.Management.Automation/engine/remoting/commands/getrunspacecommand.cs index d1fc8ef531a..080c05c9d63 100644 --- a/src/System.Management.Automation/engine/remoting/commands/getrunspacecommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/getrunspacecommand.cs @@ -343,10 +343,7 @@ protected override void BeginProcessing() { base.BeginProcessing(); - if (ConfigurationName == null) - { - ConfigurationName = string.Empty; - } + ConfigurationName ??= string.Empty; } /// diff --git a/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs b/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs index d4a0a3e7508..3ea381d6a1c 100644 --- a/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs @@ -385,11 +385,7 @@ public void Dispose() /// /// Adds forwarded events to the local queue. /// - private void OnRunspacePSEventReceived(object sender, PSEventArgs e) - { - if (this.Events != null) - this.Events.AddForwardedEvent(e); - } + private void OnRunspacePSEventReceived(object sender, PSEventArgs e) => this.Events?.AddForwardedEvent(e); /// /// When the client remote session reports a URI redirection, this method will report the @@ -524,10 +520,7 @@ private void HandleRunspaceStateChanged(object sender, OperationStateEventArgs s } } - if (reason == null) - { - reason = new RuntimeException(this.GetMessage(RemotingErrorIdStrings.RemoteRunspaceOpenUnknownState, state)); - } + reason ??= new RuntimeException(this.GetMessage(RemotingErrorIdStrings.RemoteRunspaceOpenUnknownState, state)); string fullyQualifiedErrorId = WSManTransportManagerUtils.GetFQEIDFromTransportError( transErrorCode, @@ -647,11 +640,11 @@ private List CreateRunspacesWhenRunspaceParameterSpecified() if (remoteRunspace.ConnectionInfo is VMConnectionInfo) { - newConnectionInfo = remoteRunspace.ConnectionInfo.InternalCopy(); + newConnectionInfo = remoteRunspace.ConnectionInfo.Clone(); } else if (remoteRunspace.ConnectionInfo is ContainerConnectionInfo) { - ContainerConnectionInfo newContainerConnectionInfo = remoteRunspace.ConnectionInfo.InternalCopy() as ContainerConnectionInfo; + ContainerConnectionInfo newContainerConnectionInfo = remoteRunspace.ConnectionInfo.Clone() as ContainerConnectionInfo; newContainerConnectionInfo.CreateContainerProcess(); newConnectionInfo = newContainerConnectionInfo; } @@ -1093,7 +1086,8 @@ private List CreateRunspacesForSSHHostParameterSet() this.KeyFilePath, port, Subsystem, - ConnectingTimeout); + ConnectingTimeout, + Options); var typeTable = TypeTable.LoadDefaultTypeFiles(); string rsName = GetRunspaceName(index, out int rsIdUnused); index++; @@ -1120,7 +1114,8 @@ private List CreateRunspacesForSSHHostHashParameterSet() sshConnection.KeyFilePath, sshConnection.Port, sshConnection.Subsystem, - sshConnection.ConnectingTimeout); + sshConnection.ConnectingTimeout, + sshConnection.Options); var typeTable = TypeTable.LoadDefaultTypeFiles(); string rsName = GetRunspaceName(index, out int rsIdUnused); index++; diff --git a/src/System.Management.Automation/engine/remoting/commands/remotingcommandutil.cs b/src/System.Management.Automation/engine/remoting/commands/remotingcommandutil.cs index 094dcedc6ad..f30e4ba5109 100644 --- a/src/System.Management.Automation/engine/remoting/commands/remotingcommandutil.cs +++ b/src/System.Management.Automation/engine/remoting/commands/remotingcommandutil.cs @@ -165,60 +165,5 @@ internal static void CheckHostRemotingPrerequisites() throw new InvalidOperationException(errorRecord.ToString()); } } - - internal static void CheckPSVersion(Version version) - { - // PSVersion value can only be 2.0, 3.0, 4.0, 5.0, or 5.1 - if (version != null) - { - // PSVersion value can only be 2.0, 3.0, 4.0, 5.0, or 5.1 - if (!(version.Major >= 2 && version.Major <= 4 && version.Minor == 0) && - !(version.Major == 5 && version.Minor <= 1)) - { - throw new ArgumentException( - StringUtil.Format(RemotingErrorIdStrings.PSVersionParameterOutOfRange, version, "PSVersion")); - } - } - } - - /// - /// Checks if the specified version of PowerShell is installed. - /// - /// - internal static void CheckIfPowerShellVersionIsInstalled(Version version) - { - // Check if PowerShell 2.0 is installed - if (version != null && version.Major == 2) - { -#if CORECLR - // PowerShell 2.0 is not available for CoreCLR - throw new ArgumentException( - PSRemotingErrorInvariants.FormatResourceString( - RemotingErrorIdStrings.PowerShellNotInstalled, - version, "PSVersion")); -#else - // Because of app-compat issues, in Win8, we will have PS 2.0 installed by default but not .NET 2.0 - // In such a case, it is not enough if we check just PowerShell registry keys. We also need to check if .NET 2.0 is installed. - try - { - RegistryKey engineKey = PSSnapInReader.GetPSEngineKey(PSVersionInfo.RegistryVersion1Key); - // Also check for .NET 2.0 installation - if (!PsUtils.FrameworkRegistryInstallation.IsFrameworkInstalled(2, 0, 0)) - { - throw new ArgumentException( - PSRemotingErrorInvariants.FormatResourceString( - RemotingErrorIdStrings.NetFrameWorkV2NotInstalled)); - } - } - catch (PSArgumentException) - { - throw new ArgumentException( - PSRemotingErrorInvariants.FormatResourceString( - RemotingErrorIdStrings.PowerShellNotInstalled, - version, "PSVersion")); - } -#endif - } - } } } diff --git a/src/System.Management.Automation/engine/remoting/common/RemoteSessionNamedPipe.cs b/src/System.Management.Automation/engine/remoting/common/RemoteSessionNamedPipe.cs index 2c154f79990..2c8b6a33ce7 100644 --- a/src/System.Management.Automation/engine/remoting/common/RemoteSessionNamedPipe.cs +++ b/src/System.Management.Automation/engine/remoting/common/RemoteSessionNamedPipe.cs @@ -504,10 +504,7 @@ private static NamedPipeServerStream CreateNamedPipe( securityAttributes); int lastError = Marshal.GetLastWin32Error(); - if (securityDescHandle != null) - { - securityDescHandle.Value.Free(); - } + securityDescHandle?.Free(); if (pipeHandle.IsInvalid) { @@ -1009,8 +1006,6 @@ internal class NamedPipeClientBase : IDisposable private NamedPipeClientStream _clientPipeStream; private readonly PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource(); - protected string _pipeName; - #endregion #region Properties @@ -1030,25 +1025,30 @@ internal class NamedPipeClientBase : IDisposable /// public string PipeName { - get { return _pipeName; } + get; + internal set; } #endregion - #region Constructor - - public NamedPipeClientBase() - { } - - #endregion - #region IDisposable /// - /// Dispose. + /// Dispose object. /// public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (!disposing) + { + return; + } + if (TextReader != null) { try { TextReader.Dispose(); } @@ -1093,23 +1093,23 @@ public void Connect( TextWriter.AutoFlush = true; _tracer.WriteMessage("NamedPipeClientBase", "Connect", Guid.Empty, - "Connection started on pipe: {0}", _pipeName); + "Connection started on pipe: {0}", PipeName); } /// /// Closes the named pipe. /// - public void Close() - { - if (_clientPipeStream != null) - { - _clientPipeStream.Dispose(); - } - } + public void Close() => _clientPipeStream?.Dispose(); + /// + /// Abort connection attempt. + /// public virtual void AbortConnect() { } + /// + /// Begin connection attempt. + /// protected virtual NamedPipeClientStream DoConnect(int timeout) { return null; @@ -1166,7 +1166,7 @@ internal RemoteSessionNamedPipeClient( throw new PSArgumentNullException(nameof(pipeName)); } - _pipeName = pipeName; + PipeName = pipeName; // Defer creating the .Net NamedPipeClientStream object until we connect. // _clientPipeStream == null. @@ -1189,7 +1189,7 @@ internal RemoteSessionNamedPipeClient( if (coreName == null) { throw new PSArgumentNullException(nameof(coreName)); } - _pipeName = @"\\" + serverName + @"\" + namespaceName + @"\" + coreName; + PipeName = @"\\" + serverName + @"\" + namespaceName + @"\" + coreName; // Defer creating the .Net NamedPipeClientStream object until we connect. // _clientPipeStream == null. @@ -1211,6 +1211,9 @@ public override void AbortConnect() #region Protected Methods + /// + /// Begin connection attempt. + /// protected override NamedPipeClientStream DoConnect(int timeout) { // Repeatedly attempt connection to pipe until timeout expires. @@ -1220,11 +1223,11 @@ protected override NamedPipeClientStream DoConnect(int timeout) NamedPipeClientStream namedPipeClientStream = new NamedPipeClientStream( serverName: ".", - pipeName: _pipeName, + pipeName: PipeName, direction: PipeDirection.InOut, options: PipeOptions.Asynchronous); - namedPipeClientStream.Connect(); + namedPipeClientStream.ConnectAsync(timeout); do { @@ -1275,7 +1278,7 @@ public ContainerSessionNamedPipeClient( // // Named pipe inside Windows Server container is under different name space. // - _pipeName = containerObRoot + @"\Device\NamedPipe\" + + PipeName = containerObRoot + @"\Device\NamedPipe\" + NamedPipeUtils.CreateProcessPipeName(procId, appDomainName); } @@ -1301,7 +1304,7 @@ protected override NamedPipeClientStream DoConnect(int timeout) { // Get handle to pipe. pipeHandle = NamedPipeNative.CreateFile( - lpFileName: _pipeName, + lpFileName: PipeName, dwDesiredAccess: NamedPipeNative.GENERIC_READ | NamedPipeNative.GENERIC_WRITE, dwShareMode: 0, SecurityAttributes: IntPtr.Zero, diff --git a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs index 49763f43a13..088bdddcb32 100644 --- a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs +++ b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Collections; using System.Collections.Generic; using System.ComponentModel; // Win32Exception using System.Diagnostics; @@ -325,13 +326,33 @@ internal int TimeSpanToTimeOutMs(TimeSpan t) } } + /// + /// Validates port number is in range. + /// + /// Port number to validate. + internal virtual void ValidatePortInRange(int port) + { + if ((port < MinPort || port > MaxPort)) + { + string message = + PSRemotingErrorInvariants.FormatResourceString( + RemotingErrorIdStrings.PortIsOutOfRange, port); + ArgumentException e = new ArgumentException(message); + throw e; + } + } + + #endregion + + #region Public methods + /// /// Creates the appropriate client session transportmanager. /// /// Runspace/Pool instance Id. /// Session name. /// PSRemotingCryptoHelper. - internal virtual BaseClientSessionTransportManager CreateClientSessionTransportManager( + public virtual BaseClientSessionTransportManager CreateClientSessionTransportManager( Guid instanceId, string sessionName, PSRemotingCryptoHelper cryptoHelper) @@ -343,27 +364,11 @@ internal virtual BaseClientSessionTransportManager CreateClientSessionTransportM /// Create a copy of the connection info object. /// /// Copy of the connection info object. - internal virtual RunspaceConnectionInfo InternalCopy() + public virtual RunspaceConnectionInfo Clone() { throw new PSNotImplementedException(); } - /// - /// Validates port number is in range. - /// - /// Port number to validate. - internal virtual void ValidatePortInRange(int port) - { - if ((port < MinPort || port > MaxPort)) - { - string message = - PSRemotingErrorInvariants.FormatResourceString( - RemotingErrorIdStrings.PortIsOutOfRange, port); - ArgumentException e = new ArgumentException(message); - throw e; - } - } - #endregion #region Constants @@ -1062,10 +1067,10 @@ public override void SetSessionOptions(PSSessionOption options) } /// - /// Shallow copy of the current instance. + /// Create a copy of the connection info object. /// - /// RunspaceConnectionInfo. - internal override RunspaceConnectionInfo InternalCopy() + /// Copy of the connection info object. + public override RunspaceConnectionInfo Clone() { return Copy(); } @@ -1133,7 +1138,14 @@ public WSManConnectionInfo Copy() #region Internal Methods - internal override BaseClientSessionTransportManager CreateClientSessionTransportManager(Guid instanceId, string sessionName, PSRemotingCryptoHelper cryptoHelper) + /// + /// Creates the appropriate client session transportmanager. + /// + /// Runspace/Pool instance Id. + /// Session name. + /// PSRemotingCryptoHelper instance. + /// Instance of WSManClientSessionTransportManager + public override BaseClientSessionTransportManager CreateClientSessionTransportManager(Guid instanceId, string sessionName, PSRemotingCryptoHelper cryptoHelper) { return new WSManClientSessionTransportManager( instanceId, @@ -1653,12 +1665,23 @@ public NewProcessConnectionInfo Copy() return result; } - internal override RunspaceConnectionInfo InternalCopy() + /// + /// Create a copy of the connection info object. + /// + /// Copy of the connection info object. + public override RunspaceConnectionInfo Clone() { return Copy(); } - internal override BaseClientSessionTransportManager CreateClientSessionTransportManager(Guid instanceId, string sessionName, PSRemotingCryptoHelper cryptoHelper) + /// + /// Creates the appropriate client session transportmanager. + /// + /// Runspace/Pool instance Id. + /// Session name. + /// PSRemotingCryptoHelper object. + /// Instance of OutOfProcessClientSessionTransportManager + public override BaseClientSessionTransportManager CreateClientSessionTransportManager(Guid instanceId, string sessionName, PSRemotingCryptoHelper cryptoHelper) { return new OutOfProcessClientSessionTransportManager( instanceId, @@ -1873,10 +1896,10 @@ public override string CertificateThumbprint } /// - /// Shallow copy of current instance. + /// Create a copy of the connection info object. /// - /// NamedPipeConnectionInfo. - internal override RunspaceConnectionInfo InternalCopy() + /// Copy of the connection info object. + public override RunspaceConnectionInfo Clone() { NamedPipeConnectionInfo newCopy = new NamedPipeConnectionInfo(); newCopy._authMechanism = this.AuthenticationMechanism; @@ -1889,7 +1912,14 @@ internal override RunspaceConnectionInfo InternalCopy() return newCopy; } - internal override BaseClientSessionTransportManager CreateClientSessionTransportManager(Guid instanceId, string sessionName, PSRemotingCryptoHelper cryptoHelper) + /// + /// Creates the appropriate client session transportmanager. + /// + /// Runspace/Pool instance Id. + /// Session name. + /// PSRemotingCryptoHelper object. + /// Instance of NamedPipeClientSessionTransportManager + public override BaseClientSessionTransportManager CreateClientSessionTransportManager(Guid instanceId, string sessionName, PSRemotingCryptoHelper cryptoHelper) { return new NamedPipeClientSessionTransportManager( this, @@ -1935,7 +1965,7 @@ public string UserName /// /// Key File Path. /// - private string KeyFilePath + public string KeyFilePath { get; set; @@ -1944,7 +1974,7 @@ private string KeyFilePath /// /// Port for connection. /// - private int Port + public int Port { get; set; @@ -1953,7 +1983,7 @@ private int Port /// /// Subsystem to use. /// - private string Subsystem + public string Subsystem { get; set; @@ -1969,18 +1999,27 @@ public int ConnectingTimeout set; } + /// The SSH options to pass to OpenSSH. + /// Gets or sets the SSH options to pass to OpenSSH. + /// + private Hashtable Options + { + get; + set; + } + #endregion #region Constructors /// - /// Constructor. + /// Initializes a new instance of the class. /// private SSHConnectionInfo() { } /// - /// Constructor. + /// Initializes a new instance of the class. /// /// User Name. /// Computer Name. @@ -2001,7 +2040,7 @@ public SSHConnectionInfo( } /// - /// Constructor. + /// Initializes a new instance of the class. /// /// User Name. /// Computer Name. @@ -2018,7 +2057,7 @@ public SSHConnectionInfo( } /// - /// Constructor. + /// Initializes a new instance of the class. /// /// User Name. /// Computer Name. @@ -2055,6 +2094,28 @@ public SSHConnectionInfo( ConnectingTimeout = connectingTimeout; } + /// + /// Initializes a new instance of the class. + /// + /// User Name. + /// Computer Name. + /// Key File Path. + /// Port number for connection (default 22). + /// Subsystem to use (default 'powershell'). + /// Timeout time for terminating connection attempt. + /// Options for the SSH connection. + public SSHConnectionInfo( + string userName, + string computerName, + string keyFilePath, + int port, + string subsystem, + int connectingTimeout, + Hashtable options) : this(userName, computerName, keyFilePath, port, subsystem, connectingTimeout) + { + Options = options; + } + #endregion #region Overrides @@ -2099,10 +2160,10 @@ public override string CertificateThumbprint } /// - /// Shallow copy of current instance. + /// Create a copy of the connection info object. /// - /// NamedPipeConnectionInfo. - internal override RunspaceConnectionInfo InternalCopy() + /// Copy of the connection info object. + public override RunspaceConnectionInfo Clone() { SSHConnectionInfo newCopy = new SSHConnectionInfo(); newCopy.ComputerName = ComputerName; @@ -2111,18 +2172,18 @@ internal override RunspaceConnectionInfo InternalCopy() newCopy.Port = Port; newCopy.Subsystem = Subsystem; newCopy.ConnectingTimeout = ConnectingTimeout; + newCopy.Options = Options; return newCopy; } /// - /// CreateClientSessionTransportManager. + /// Creates the appropriate client session transportmanager. /// - /// - /// - /// - /// - internal override BaseClientSessionTransportManager CreateClientSessionTransportManager(Guid instanceId, string sessionName, PSRemotingCryptoHelper cryptoHelper) + /// Runspace/Pool instance Id. + /// Session name. + /// PSRemotingCryptoHelper. + public override BaseClientSessionTransportManager CreateClientSessionTransportManager(Guid instanceId, string sessionName, PSRemotingCryptoHelper cryptoHelper) { return new SSHClientSessionTransportManager( this, @@ -2163,9 +2224,9 @@ internal int StartSSHProcess( // // Local ssh invoked as: // windows: - // ssh.exe [-i identity_file] [-l login_name] [-p port] -s + // ssh.exe [-i identity_file] [-l login_name] [-p port] [-o option] -s // linux|macos: - // ssh [-i identity_file] [-l login_name] [-p port] -s + // ssh [-i identity_file] [-l login_name] [-p port] [-o option] -s // where is interpreted as the subsystem due to the -s flag. // // Remote sshd configured for PowerShell Remoting Protocol (PSRP) over Secure Shell Protocol (SSH) @@ -2216,6 +2277,15 @@ internal int StartSSHProcess( startInfo.ArgumentList.Add(string.Format(CultureInfo.InvariantCulture, @"-p {0}", this.Port)); } + // pass "-o option=value" command line argument to ssh if options are provided + if (this.Options != null) + { + foreach (DictionaryEntry pair in this.Options) + { + startInfo.ArgumentList.Add(string.Format(CultureInfo.InvariantCulture, @"-o {0}={1}", pair.Key, pair.Value)); + } + } + // pass "-s destination command" command line arguments to ssh where command is the subsystem to invoke on the destination // note that ssh expects IPv6 addresses to not be enclosed in square brackets so trim them if present startInfo.ArgumentList.Add(string.Format(CultureInfo.InvariantCulture, @"-s {0} {1}", this.ComputerName.TrimStart('[').TrimEnd(']'), this.Subsystem)); @@ -2227,6 +2297,10 @@ internal int StartSSHProcess( return StartSSHProcessImpl(startInfo, out stdInWriterVar, out stdOutReaderVar, out stdErrReaderVar); } + /// + /// Terminates the SSH process by process Id. + /// + /// Process id. internal void KillSSHProcess(int pid) { KillSSHProcessImpl(pid); @@ -2639,17 +2713,12 @@ private static Process CreateProcessWithRedirectedStd( } catch (Exception) { - if (stdInPipeServer != null) { stdInPipeServer.Dispose(); } - - if (stdInPipeClient != null) { stdInPipeClient.Dispose(); } - - if (stdOutPipeServer != null) { stdOutPipeServer.Dispose(); } - - if (stdOutPipeClient != null) { stdOutPipeClient.Dispose(); } - - if (stdErrPipeServer != null) { stdErrPipeServer.Dispose(); } - - if (stdErrPipeClient != null) { stdErrPipeClient.Dispose(); } + stdInPipeServer?.Dispose(); + stdInPipeClient?.Dispose(); + stdOutPipeServer?.Dispose(); + stdOutPipeClient?.Dispose(); + stdErrPipeServer?.Dispose(); + stdErrPipeClient?.Dispose(); throw; } @@ -2715,17 +2784,12 @@ private static Process CreateProcessWithRedirectedStd( } catch (Exception) { - if (stdInPipeServer != null) { stdInPipeServer.Dispose(); } - - if (stdInPipeClient != null) { stdInPipeClient.Dispose(); } - - if (stdOutPipeServer != null) { stdOutPipeServer.Dispose(); } - - if (stdOutPipeClient != null) { stdOutPipeClient.Dispose(); } - - if (stdErrPipeServer != null) { stdErrPipeServer.Dispose(); } - - if (stdErrPipeClient != null) { stdErrPipeClient.Dispose(); } + stdInPipeServer?.Dispose(); + stdInPipeClient?.Dispose(); + stdOutPipeServer?.Dispose(); + stdOutPipeClient?.Dispose(); + stdErrPipeServer?.Dispose(); + stdErrPipeClient?.Dispose(); throw; } @@ -2783,10 +2847,7 @@ private static SafePipeHandle CreateNamedPipe( securityAttributes); int lastError = Marshal.GetLastWin32Error(); - if (securityDescHandle != null) - { - securityDescHandle.Value.Free(); - } + securityDescHandle?.Free(); if (pipeHandle.IsInvalid) { @@ -2890,13 +2951,24 @@ public override PSCredential Credential /// public override string ComputerName { get; set; } - internal override RunspaceConnectionInfo InternalCopy() + /// + /// Create a copy of the connection info object. + /// + /// Copy of the connection info object. + public override RunspaceConnectionInfo Clone() { VMConnectionInfo result = new VMConnectionInfo(Credential, VMGuid, ComputerName, ConfigurationName); return result; } - internal override BaseClientSessionTransportManager CreateClientSessionTransportManager(Guid instanceId, string sessionName, PSRemotingCryptoHelper cryptoHelper) + /// + /// Creates the appropriate client session transportmanager. + /// + /// Runspace/Pool instance Id. + /// Session name. + /// PSRemotingCryptoHelper instance. + /// Instance of VMHyperVSocketClientSessionTransportManager. + public override BaseClientSessionTransportManager CreateClientSessionTransportManager(Guid instanceId, string sessionName, PSRemotingCryptoHelper cryptoHelper) { return new VMHyperVSocketClientSessionTransportManager( this, @@ -3023,13 +3095,24 @@ public override string ComputerName set { throw new PSNotSupportedException(); } } - internal override RunspaceConnectionInfo InternalCopy() + /// + /// Create a copy of the connection info object. + /// + /// Copy of the connection info object. + public override RunspaceConnectionInfo Clone() { ContainerConnectionInfo newCopy = new ContainerConnectionInfo(ContainerProc); return newCopy; } - internal override BaseClientSessionTransportManager CreateClientSessionTransportManager(Guid instanceId, string sessionName, PSRemotingCryptoHelper cryptoHelper) + /// + /// Creates the appropriate client session transportmanager. + /// + /// Runspace/Pool instance Id. + /// Session name. + /// PSRemotingCryptoHelper object. + /// Instance of ContainerHyperVSocketClientSessionTransportManager + public override BaseClientSessionTransportManager CreateClientSessionTransportManager(Guid instanceId, string sessionName, PSRemotingCryptoHelper cryptoHelper) { if (ContainerProc.RuntimeId != Guid.Empty) { diff --git a/src/System.Management.Automation/engine/remoting/common/WireDataFormat/EncodeAndDecode.cs b/src/System.Management.Automation/engine/remoting/common/WireDataFormat/EncodeAndDecode.cs index aa22e45682a..70693c4c3d9 100644 --- a/src/System.Management.Automation/engine/remoting/common/WireDataFormat/EncodeAndDecode.cs +++ b/src/System.Management.Automation/engine/remoting/common/WireDataFormat/EncodeAndDecode.cs @@ -125,234 +125,6 @@ internal static class RemoteDataNameStrings // to client to let client know if the negotiation succeeded. internal const string IsNegotiationSucceeded = "IsNegotiationSucceeded"; - #region "PSv2 Tab Expansion Function" - - internal const string PSv2TabExpansionFunction = "TabExpansion"; - - /// - /// This is the PSv2 function for tab expansion. It's only for legacy purpose - used in - /// an interactive remote session from a win7 machine to a win8 machine (or later). - /// - internal const string PSv2TabExpansionFunctionText = @" - param($line, $lastWord) - & { - function Write-Members ($sep='.') - { - Invoke-Expression ('$_val=' + $_expression) - - $_method = [Management.Automation.PSMemberTypes] ` - 'Method,CodeMethod,ScriptMethod,ParameterizedProperty' - if ($sep -eq '.') - { - $params = @{view = 'extended','adapted','base'} - } - else - { - $params = @{static=$true} - } - - foreach ($_m in ,$_val | Get-Member @params $_pat | - Sort-Object membertype,name) - { - if ($_m.MemberType -band $_method) - { - # Return a method... - $_base + $_expression + $sep + $_m.name + '(' - } - else { - # Return a property... - $_base + $_expression + $sep + $_m.name - } - } - } - - # If a command name contains any of these chars, it needs to be quoted - $_charsRequiringQuotes = ('`&@''#{}()$,;|<> ' + ""`t"").ToCharArray() - - # If a variable name contains any of these characters it needs to be in braces - $_varsRequiringQuotes = ('-`&@''#{}()$,;|<> .\/' + ""`t"").ToCharArray() - - switch -regex ($lastWord) - { - # Handle property and method expansion rooted at variables... - # e.g. $a.b. - '(^.*)(\$(\w|:|\.)+)\.([*\w]*)$' { - $_base = $matches[1] - $_expression = $matches[2] - $_pat = $matches[4] + '*' - Write-Members - break; - } - - # Handle simple property and method expansion on static members... - # e.g. [datetime]::n - '(^.*)(\[(\w|\.|\+)+\])(\:\:|\.){0,1}([*\w]*)$' { - $_base = $matches[1] - $_expression = $matches[2] - $_pat = $matches[5] + '*' - Write-Members $(if (! $matches[4]) {'::'} else {$matches[4]}) - break; - } - - # Handle complex property and method expansion on static members - # where there are intermediate properties... - # e.g. [datetime]::now.d - '(^.*)(\[(\w|\.|\+)+\](\:\:|\.)(\w+\.)+)([*\w]*)$' { - $_base = $matches[1] # everything before the expression - $_expression = $matches[2].TrimEnd('.') # expression less trailing '.' - $_pat = $matches[6] + '*' # the member to look for... - Write-Members - break; - } - - # Handle variable name expansion... - '(^.*\$)([*\w:]+)$' { - $_prefix = $matches[1] - $_varName = $matches[2] - $_colonPos = $_varname.IndexOf(':') - if ($_colonPos -eq -1) - { - $_varName = 'variable:' + $_varName - $_provider = '' - } - else - { - $_provider = $_varname.Substring(0, $_colonPos+1) - } - - foreach ($_v in Get-ChildItem ($_varName + '*') | sort Name) - { - $_nameFound = $_v.name - $(if ($_nameFound.IndexOfAny($_varsRequiringQuotes) -eq -1) {'{0}{1}{2}'} - else {'{0}{{{1}{2}}}'}) -f $_prefix, $_provider, $_nameFound - } - - break; - } - - # Do completion on parameters... - '^-([*\w0-9]*)' { - $_pat = $matches[1] + '*' - - # extract the command name from the string - # first split the string into statements and pipeline elements - # This doesn't handle strings however. - $_command = [regex]::Split($line, '[|;=]')[-1] - - # Extract the trailing unclosed block e.g. ls | foreach { cp - if ($_command -match '\{([^\{\}]*)$') - { - $_command = $matches[1] - } - - # Extract the longest unclosed parenthetical expression... - if ($_command -match '\(([^()]*)$') - { - $_command = $matches[1] - } - - # take the first space separated token of the remaining string - # as the command to look up. Trim any leading or trailing spaces - # so you don't get leading empty elements. - $_command = $_command.TrimEnd('-') - $_command,$_arguments = $_command.Trim().Split() - - # now get the info object for it, -ArgumentList will force aliases to be resolved - # it also retrieves dynamic parameters - try - { - $_command = @(Get-Command -type 'Alias,Cmdlet,Function,Filter,ExternalScript' ` - -Name $_command -ArgumentList $_arguments)[0] - } - catch - { - # see if the command is an alias. If so, resolve it to the real command - if(Test-Path alias:\$_command) - { - $_command = @(Get-Command -Type Alias $_command)[0].Definition - } - - # If we were unsuccessful retrieving the command, try again without the parameters - $_command = @(Get-Command -type 'Cmdlet,Function,Filter,ExternalScript' ` - -Name $_command)[0] - } - - # remove errors generated by the command not being found, and break - if(-not $_command) { $error.RemoveAt(0); break; } - - # expand the parameter sets and emit the matching elements - # need to use psbase.Keys in case 'keys' is one of the parameters - # to the cmdlet - foreach ($_n in $_command.Parameters.psbase.Keys) - { - if ($_n -like $_pat) { '-' + $_n } - } - - break; - } - - # Tab complete against history either # or # - '^#(\w*)' { - $_pattern = $matches[1] - if ($_pattern -match '^[0-9]+$') - { - Get-History -ea SilentlyContinue -Id $_pattern | ForEach-Object { $_.CommandLine } - } - else - { - $_pattern = '*' + $_pattern + '*' - Get-History -Count 32767 | Sort-Object -Descending Id| ForEach-Object { $_.CommandLine } | where { $_ -like $_pattern } - } - - break; - } - - # try to find a matching command... - default { - # parse the script... - $_tokens = [System.Management.Automation.PSParser]::Tokenize($line, - [ref] $null) - - if ($_tokens) - { - $_lastToken = $_tokens[$_tokens.count - 1] - if ($_lastToken.Type -eq 'Command') - { - $_cmd = $_lastToken.Content - - # don't look for paths... - if ($_cmd.IndexOfAny('/\:') -eq -1) - { - # handle parsing errors - the last token string should be the last - # string in the line... - if ($lastword.Length -ge $_cmd.Length -and - $lastword.substring($lastword.length-$_cmd.length) -eq $_cmd) - { - $_pat = $_cmd + '*' - $_base = $lastword.substring(0, $lastword.length-$_cmd.length) - - # get files in current directory first, then look for commands... - $( try {Resolve-Path -ea SilentlyContinue -Relative $_pat } catch {} ; - try { $ExecutionContext.InvokeCommand.GetCommandName($_pat, $true, $false) | - Sort-Object -Unique } catch {} ) | - # If the command contains non-word characters (space, ) ] ; ) etc.) - # then it needs to be quoted and prefixed with & - ForEach-Object { - if ($_.IndexOfAny($_charsRequiringQuotes) -eq -1) { $_ } - elseif ($_.IndexOf('''') -ge 0) {'& ''{0}''' -f $_.Replace('''','''''') } - else { '& ''{0}''' -f $_ }} | - ForEach-Object {'{0}{1}' -f $_base,$_ } - } - } - } - } - } - } - } - "; - - #endregion "PSv2 Tab Expansion Function" - #region Host Related Strings internal const string CallId = "ci"; @@ -1601,8 +1373,6 @@ internal static RemoteDataObject GenerateClientSessionCapability(RemoteSessionCa Guid runspacePoolId) { PSObject temp = GenerateSessionCapability(capability); - temp.Properties.Add( - new PSNoteProperty(RemoteDataNameStrings.TimeZone, RemoteSessionCapability.GetCurrentTimeZoneInByteFormat())); return RemoteDataObject.CreateFrom(capability.RemotingDestination, RemotingDataType.SessionCapability, runspacePoolId, Guid.Empty, temp); } @@ -2373,24 +2143,6 @@ internal static RemoteSessionCapability GetSessionCapability(object data) RemotingDestination.InvalidDestination, protocolVersion, psVersion, serializationVersion); - if (dataAsPSObject.Properties[RemoteDataNameStrings.TimeZone] != null) - { - // Binary deserialization of timezone info via BinaryFormatter is unsafe, - // so don't deserialize any untrusted client data using this API. - // - // In addition, the binary data being sent by the client doesn't represent - // the client's current TimeZone unless they somehow accessed the - // StandardName and DaylightName. These properties are initialized lazily - // by the .NET Framework, and would be populated by the server with local - // values anyways. - // - // So just return the CurrentTimeZone. - -#if !CORECLR // TimeZone Not In CoreCLR - result.TimeZone = TimeZone.CurrentTimeZone; -#endif - } - return result; } diff --git a/src/System.Management.Automation/engine/remoting/common/WireDataFormat/RemoteHost.cs b/src/System.Management.Automation/engine/remoting/common/WireDataFormat/RemoteHost.cs index c29be13487b..79b920c22b5 100644 --- a/src/System.Management.Automation/engine/remoting/common/WireDataFormat/RemoteHost.cs +++ b/src/System.Management.Automation/engine/remoting/common/WireDataFormat/RemoteHost.cs @@ -188,10 +188,7 @@ internal void ExecuteVoidMethod(PSHost clientHost) } finally { - if (remoteRunspaceToClose != null) - { - remoteRunspaceToClose.Close(); - } + remoteRunspaceToClose?.Close(); } } diff --git a/src/System.Management.Automation/engine/remoting/common/WireDataFormat/RemoteSessionCapability.cs b/src/System.Management.Automation/engine/remoting/common/WireDataFormat/RemoteSessionCapability.cs index d95ba65ec90..f0ccb496ebe 100644 --- a/src/System.Management.Automation/engine/remoting/common/WireDataFormat/RemoteSessionCapability.cs +++ b/src/System.Management.Automation/engine/remoting/common/WireDataFormat/RemoteSessionCapability.cs @@ -5,7 +5,6 @@ using System.IO; using System.Management.Automation.Host; using System.Management.Automation.Internal.Host; -using System.Runtime.Serialization.Formatters.Binary; using Dbg = System.Management.Automation.Diagnostics; @@ -25,8 +24,6 @@ internal class RemoteSessionCapability private readonly Version _serversion; private Version _protocolVersion; private readonly RemotingDestination _remotingDestination; - private static byte[] _timeZoneInByteFormat; - private TimeZoneInfo _timeZone; #endregion @@ -91,64 +88,6 @@ internal static RemoteSessionCapability CreateServerCapability() { return new RemoteSessionCapability(RemotingDestination.Client); } - - /// - /// This is static property which gets Current TimeZone in byte format - /// by using ByteFormatter. - /// This is static to make client generate this only once. - /// - internal static byte[] GetCurrentTimeZoneInByteFormat() - { - if (_timeZoneInByteFormat == null) - { - Exception e = null; - try - { - BinaryFormatter formatter = new BinaryFormatter(); - using (MemoryStream stream = new MemoryStream()) - { -#pragma warning disable SYSLIB0011 - formatter.Serialize(stream, TimeZoneInfo.Local); -#pragma warning restore SYSLIB0011 - stream.Seek(0, SeekOrigin.Begin); - byte[] result = new byte[stream.Length]; - stream.Read(result, 0, (int)stream.Length); - _timeZoneInByteFormat = result; - } - } - catch (ArgumentNullException ane) - { - e = ane; - } - catch (System.Runtime.Serialization.SerializationException sre) - { - e = sre; - } - catch (System.Security.SecurityException se) - { - e = se; - } - - // if there is any exception serializing the timezone information - // ignore it and dont try to serialize again. - if (e != null) - { - _timeZoneInByteFormat = Array.Empty(); - } - } - - return _timeZoneInByteFormat; - } - - /// - /// Gets the TimeZone of the destination machine. This may be null. - /// - internal TimeZoneInfo TimeZone - { - get { return _timeZone; } - - set { _timeZone = value; } - } } /// diff --git a/src/System.Management.Automation/engine/remoting/common/psstreamobject.cs b/src/System.Management.Automation/engine/remoting/common/psstreamobject.cs index 4e755893886..c0052b3df29 100644 --- a/src/System.Management.Automation/engine/remoting/common/psstreamobject.cs +++ b/src/System.Management.Automation/engine/remoting/common/psstreamobject.cs @@ -120,10 +120,7 @@ public void WriteStreamObject(Cmdlet cmdlet, bool overrideInquire = false) ErrorRecord errorRecord = (ErrorRecord)this.Value; errorRecord.PreserveInvocationInfoOnce = true; MshCommandRuntime mshCommandRuntime = cmdlet.CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteError(errorRecord, overrideInquire); - } + mshCommandRuntime?.WriteError(errorRecord, overrideInquire); } break; @@ -133,10 +130,7 @@ public void WriteStreamObject(Cmdlet cmdlet, bool overrideInquire = false) string debug = (string)Value; DebugRecord debugRecord = new DebugRecord(debug); MshCommandRuntime mshCommandRuntime = cmdlet.CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteDebug(debugRecord, overrideInquire); - } + mshCommandRuntime?.WriteDebug(debugRecord, overrideInquire); } break; @@ -146,10 +140,7 @@ public void WriteStreamObject(Cmdlet cmdlet, bool overrideInquire = false) string warning = (string)Value; WarningRecord warningRecord = new WarningRecord(warning); MshCommandRuntime mshCommandRuntime = cmdlet.CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteWarning(warningRecord, overrideInquire); - } + mshCommandRuntime?.WriteWarning(warningRecord, overrideInquire); } break; @@ -159,10 +150,7 @@ public void WriteStreamObject(Cmdlet cmdlet, bool overrideInquire = false) string verbose = (string)Value; VerboseRecord verboseRecord = new VerboseRecord(verbose); MshCommandRuntime mshCommandRuntime = cmdlet.CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteVerbose(verboseRecord, overrideInquire); - } + mshCommandRuntime?.WriteVerbose(verboseRecord, overrideInquire); } break; @@ -170,10 +158,7 @@ public void WriteStreamObject(Cmdlet cmdlet, bool overrideInquire = false) case PSStreamObjectType.Progress: { MshCommandRuntime mshCommandRuntime = cmdlet.CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteProgress((ProgressRecord)Value, overrideInquire); - } + mshCommandRuntime?.WriteProgress((ProgressRecord)Value, overrideInquire); } break; @@ -181,10 +166,7 @@ public void WriteStreamObject(Cmdlet cmdlet, bool overrideInquire = false) case PSStreamObjectType.Information: { MshCommandRuntime mshCommandRuntime = cmdlet.CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteInformation((InformationRecord)Value, overrideInquire); - } + mshCommandRuntime?.WriteInformation((InformationRecord)Value, overrideInquire); } break; @@ -193,10 +175,7 @@ public void WriteStreamObject(Cmdlet cmdlet, bool overrideInquire = false) { WarningRecord warningRecord = (WarningRecord)Value; MshCommandRuntime mshCommandRuntime = cmdlet.CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.AppendWarningVarList(warningRecord); - } + mshCommandRuntime?.AppendWarningVarList(warningRecord); } break; @@ -311,10 +290,7 @@ internal void WriteStreamObject(Cmdlet cmdlet, Guid instanceId, bool overrideInq errorRecord.PreserveInvocationInfoOnce = true; MshCommandRuntime mshCommandRuntime = cmdlet.CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteError(errorRecord, overrideInquire); - } + mshCommandRuntime?.WriteError(errorRecord, overrideInquire); } break; @@ -324,10 +300,7 @@ internal void WriteStreamObject(Cmdlet cmdlet, Guid instanceId, bool overrideInq string warning = (string)Value; WarningRecord warningRecord = new WarningRecord(warning); MshCommandRuntime mshCommandRuntime = cmdlet.CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteWarning(warningRecord, overrideInquire); - } + mshCommandRuntime?.WriteWarning(warningRecord, overrideInquire); } break; @@ -337,10 +310,7 @@ internal void WriteStreamObject(Cmdlet cmdlet, Guid instanceId, bool overrideInq string verbose = (string)Value; VerboseRecord verboseRecord = new VerboseRecord(verbose); MshCommandRuntime mshCommandRuntime = cmdlet.CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteVerbose(verboseRecord, overrideInquire); - } + mshCommandRuntime?.WriteVerbose(verboseRecord, overrideInquire); } break; @@ -365,10 +335,7 @@ internal void WriteStreamObject(Cmdlet cmdlet, Guid instanceId, bool overrideInq } MshCommandRuntime mshCommandRuntime = cmdlet.CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteProgress(progressRecord, overrideInquire); - } + mshCommandRuntime?.WriteProgress(progressRecord, overrideInquire); } break; @@ -378,10 +345,7 @@ internal void WriteStreamObject(Cmdlet cmdlet, Guid instanceId, bool overrideInq string debug = (string)Value; DebugRecord debugRecord = new DebugRecord(debug); MshCommandRuntime mshCommandRuntime = cmdlet.CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteDebug(debugRecord, overrideInquire); - } + mshCommandRuntime?.WriteDebug(debugRecord, overrideInquire); } break; @@ -411,10 +375,7 @@ internal void WriteStreamObject(Cmdlet cmdlet, Guid instanceId, bool overrideInq } MshCommandRuntime mshCommandRuntime = cmdlet.CommandRuntime as MshCommandRuntime; - if (mshCommandRuntime != null) - { - mshCommandRuntime.WriteInformation(informationRecord, overrideInquire); - } + mshCommandRuntime?.WriteInformation(informationRecord, overrideInquire); } break; @@ -470,10 +431,7 @@ private static void InvokeCmdletMethodAndWaitForResults(CmdletMethodInvoker diff --git a/src/System.Management.Automation/engine/remoting/fanin/BaseTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/BaseTransportManager.cs index 5d12914074c..57268568165 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/BaseTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/BaseTransportManager.cs @@ -33,27 +33,81 @@ namespace System.Management.Automation.Remoting { #region TransportErrorOccuredEventArgs - internal enum TransportMethodEnum + /// + /// Transport method for error reporting. + /// + public enum TransportMethodEnum { + /// + /// CreateShellEx + /// CreateShellEx = 0, + + /// + /// RunShellCommandEx + /// RunShellCommandEx = 1, + + /// + /// SendShellInputEx + /// SendShellInputEx = 2, + + /// + /// ReceiveShellOutputEx + /// ReceiveShellOutputEx = 3, + + /// + /// CloseShellOperationEx + /// CloseShellOperationEx = 4, + + /// + /// CommandInputEx + /// CommandInputEx = 5, + + /// + /// ReceiveCommandOutputEx + /// ReceiveCommandOutputEx = 6, + + /// + /// DisconnectShellEx + /// DisconnectShellEx = 7, + + /// + /// ReconnectShellEx + /// ReconnectShellEx = 8, + + /// + /// ConnectShellEx + /// ConnectShellEx = 9, + + /// + /// ReconnectShellCommandEx + /// ReconnectShellCommandEx = 10, + + /// + /// ConnectShellCommandEx + /// ConnectShellCommandEx = 11, + + /// + /// Unknown + /// Unknown = 12, } /// - /// Event arguments passed to TransportErrorOccured handlers. + /// Event arguments passed to TransportErrorOccurred handlers. /// - internal class TransportErrorOccuredEventArgs : EventArgs + public sealed class TransportErrorOccuredEventArgs : EventArgs { /// /// Constructor. @@ -62,9 +116,10 @@ internal class TransportErrorOccuredEventArgs : EventArgs /// Error occurred. /// /// - /// The transport method that raised the error + /// The transport method that raised the error. /// - internal TransportErrorOccuredEventArgs(PSRemotingTransportException e, + public TransportErrorOccuredEventArgs( + PSRemotingTransportException e, TransportMethodEnum m) { Exception = e; @@ -136,7 +191,7 @@ internal CreateCompleteEventArgs( /// Contains implementation that is common to both client and server /// transport managers. /// - internal abstract class BaseTransportManager : IDisposable + public abstract class BaseTransportManager : IDisposable { #region tracer @@ -206,7 +261,7 @@ internal abstract class BaseTransportManager : IDisposable #region Constructor - protected BaseTransportManager(PSRemotingCryptoHelper cryptoHelper) + internal BaseTransportManager(PSRemotingCryptoHelper cryptoHelper) { CryptoHelper = cryptoHelper; // create a common fragmentor used by this transport manager to send and receive data. @@ -361,7 +416,7 @@ public void MigrateDataReadyEventHandlers(BaseTransportManager transportManager) /// Raise the error handlers. /// /// - internal virtual void RaiseErrorHandler(TransportErrorOccuredEventArgs eventArgs) + public virtual void RaiseErrorHandler(TransportErrorOccuredEventArgs eventArgs) { WSManTransportErrorOccured.SafeInvoke(this, eventArgs); } @@ -393,7 +448,10 @@ public void Dispose() System.GC.SuppressFinalize(this); } - internal virtual void Dispose(bool isDisposing) + /// + /// Dispose resources. + /// + protected virtual void Dispose(bool isDisposing) { if (isDisposing) { @@ -407,18 +465,21 @@ internal virtual void Dispose(bool isDisposing) namespace System.Management.Automation.Remoting.Client { - internal abstract class BaseClientTransportManager : BaseTransportManager, IDisposable + /// + /// Remoting base client transport manager. + /// + public abstract class BaseClientTransportManager : BaseTransportManager, IDisposable { #region Tracer [TraceSourceAttribute("ClientTransport", "Traces ClientTransportManager")] - protected static PSTraceSource tracer = PSTraceSource.GetTracer("ClientTransport", "Traces ClientTransportManager"); + internal static PSTraceSource tracer = PSTraceSource.GetTracer("ClientTransport", "Traces ClientTransportManager"); #endregion #region Data - protected bool isClosed; - protected object syncObject = new object(); - protected PrioritySendDataCollection dataToBeSent; + internal bool isClosed; + internal object syncObject = new object(); + internal PrioritySendDataCollection dataToBeSent; // used to handle callbacks from the server..these are used to synchronize received callbacks private readonly Queue _callbackNotificationQueue; private readonly ReceiveDataCollection.OnDataAvailableCallback _onDataAvailableCallback; @@ -429,13 +490,13 @@ internal abstract class BaseClientTransportManager : BaseTransportManager, IDisp // this is used log crimson messages. // keeps track of whether a receive request has been placed on transport - protected bool receiveDataInitiated; + internal bool receiveDataInitiated; #endregion #region Constructors - protected BaseClientTransportManager(Guid runspaceId, PSRemotingCryptoHelper cryptoHelper) + internal BaseClientTransportManager(Guid runspaceId, PSRemotingCryptoHelper cryptoHelper) : base(cryptoHelper) { RunspacePoolInstanceId = runspaceId; @@ -933,14 +994,17 @@ internal class CallbackNotificationInformation #region Abstract / Virtual methods - internal abstract void CreateAsync(); + /// + /// Create the transport manager and initiate connection. + /// + public abstract void CreateAsync(); internal abstract void ConnectAsync(); /// /// The caller should make sure the call is synchronized. /// - internal virtual void CloseAsync() + public virtual void CloseAsync() { // Clear the send collection dataToBeSent.Clear(); @@ -998,7 +1062,10 @@ internal virtual void PrepareForConnect() } } - internal override void Dispose(bool isDisposing) + /// + /// Dispose resources. + /// + protected override void Dispose(bool isDisposing) { // clear event handlers this.CreateCompleted = null; @@ -1014,11 +1081,14 @@ internal override void Dispose(bool isDisposing) #endregion } - internal abstract class BaseClientSessionTransportManager : BaseClientTransportManager, IDisposable + /// + /// Remoting base client session transport manager. + /// + public abstract class BaseClientSessionTransportManager : BaseClientTransportManager, IDisposable { #region Constructors - protected BaseClientSessionTransportManager(Guid runspaceId, PSRemotingCryptoHelper cryptoHelper) + internal BaseClientSessionTransportManager(Guid runspaceId, PSRemotingCryptoHelper cryptoHelper) : base(runspaceId, cryptoHelper) { } @@ -1167,7 +1237,7 @@ internal void RaiseSignalCompleted() #region Overrides - internal override void Dispose(bool isDisposing) + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -1289,10 +1359,7 @@ internal void SendDataToClient(RemoteDataObject data, bool flush, bool rep data.Data); if (_isSerializing) { - if (_dataToBeSentQueue == null) - { - _dataToBeSentQueue = new Queue>(); - } + _dataToBeSentQueue ??= new Queue>(); _dataToBeSentQueue.Enqueue(new Tuple(dataToBeSent, flush, reportPending)); return; diff --git a/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs b/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs index 9e751dcdb7d..088685c28e0 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs @@ -22,6 +22,8 @@ namespace System.Management.Automation.Remoting { + #region WSMan endpoint configuration + /// /// This struct is used to represent contents from configuration xml. The /// XML is passed to plugins by WSMan API. @@ -56,6 +58,8 @@ internal class ConfigurationDataFromXML #endregion + #region Fields + internal string StartupScript; // this field is used only by an Out-Of-Process (IPC) server process internal string InitializationScriptForOutOfProcessRunspace; @@ -71,6 +75,10 @@ internal class ConfigurationDataFromXML internal PSSessionConfigurationData SessionConfigurationData; internal string ConfigFilePath; + #endregion + + #region Methods + /// /// Using optionName and optionValue updates the current object. /// @@ -278,15 +286,9 @@ internal static ConfigurationDataFromXML Create(string initializationParameters) } // assign defaults after parsing the xml content. - if (result.MaxReceivedObjectSizeMB == null) - { - result.MaxReceivedObjectSizeMB = BaseTransportManager.MaximumReceivedObjectSize; - } + result.MaxReceivedObjectSizeMB ??= BaseTransportManager.MaximumReceivedObjectSize; - if (result.MaxReceivedCommandSizeMB == null) - { - result.MaxReceivedCommandSizeMB = BaseTransportManager.MaximumReceivedDataSize; - } + result.MaxReceivedCommandSizeMB ??= BaseTransportManager.MaximumReceivedDataSize; return result; } @@ -324,6 +326,8 @@ internal PSSessionConfiguration CreateEndPointConfigurationInstance() throw PSTraceSource.NewArgumentException("typeToLoad", RemotingErrorIdStrings.UnableToLoadType, EndPointConfigurationTypeName, ConfigurationDataFromXML.INITPARAMETERSTOKEN); } + + #endregion } /// @@ -450,7 +454,8 @@ protected virtual void Dispose(bool isDisposing) ... */ - internal static ConfigurationDataFromXML LoadEndPointConfiguration(string shellId, + internal static ConfigurationDataFromXML LoadEndPointConfiguration( + string shellId, string initializationParameters) { ConfigurationDataFromXML configData = null; @@ -798,6 +803,8 @@ private static string /// internal sealed class DefaultRemotePowerShellConfiguration : PSSessionConfiguration { + #region Method overrides + /// /// /// @@ -852,9 +859,15 @@ public override InitialSessionState GetInitialSessionState(PSSessionConfiguratio return sessionState; } + + #endregion } - #region Declarative Initial Session Configuration + #endregion + + #region Declarative InitialSession Configuration + + #region Supporting types /// /// Specifies type of initial session state to use. Valid values are Empty and Default. @@ -898,6 +911,10 @@ internal ConfigTypeEntry(string key, TypeValidationCallback callback) } } + #endregion + + #region ConfigFileConstants + /// /// Configuration file constants. /// @@ -1355,6 +1372,8 @@ private static bool StringOrHashtableArrayTypeValidationCallback(string key, obj } } + #endregion + #region DISC Utilities /// @@ -1681,6 +1700,8 @@ internal static void ValidateRoleDefinitions(IDictionary roleDefinitions) #endregion + #region DISCPowerShellConfiguration + /// /// Creates an initial session state based on the configuration language for PSSC files. /// @@ -1706,13 +1727,14 @@ internal Hashtable ConfigHash /// target session. If you have a WindowsPrincipal for a user, for example, create a Function that /// checks windowsPrincipal.IsInRole(). /// - internal DISCPowerShellConfiguration(string configFile, Func roleVerifier) + /// Validate file for supported configuration options. + internal DISCPowerShellConfiguration( + string configFile, + Func roleVerifier, + bool validateFile = false) { _configFile = configFile; - if (roleVerifier == null) - { - roleVerifier = static (role) => false; - } + roleVerifier ??= static (role) => false; Runspace backupRunspace = Runspace.DefaultRunspace; @@ -1726,6 +1748,12 @@ internal DISCPowerShellConfiguration(string configFile, Func roleV configFile, out scriptName); _configHash = DISCUtils.LoadConfigFile(Runspace.DefaultRunspace.ExecutionContext, script); + + if (validateFile) + { + DISCFileValidation.ValidateContents(_configHash); + } + MergeRoleRulesIntoConfigHash(roleVerifier); MergeRoleCapabilitiesIntoConfigHash(); @@ -2446,7 +2474,7 @@ private static void ProcessVisibleCommands(InitialSessionState iss, object[] com // Parameters = A dictionary of parameter names -> Modifications // Modifications = A dictionary of modification types (ValidatePattern, ValidateSet) to the interim value // for that attribute, as a HashSet of strings. For ValidateSet, this will be used as a collection of strings - // directly during proxy generation. For For ValidatePattern, it will be combined into a regex + // directly during proxy generation. For ValidatePattern, it will be combined into a regex // like: '^(Pattern1|Pattern2|Pattern3)$' during proxy generation. Dictionary commandModifications = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -2890,4 +2918,110 @@ internal static T[] TryGetObjectsOfType(object hashObj, IEnumerable typ } } #endregion + + #region DISCFileValidation + + internal static class DISCFileValidation + { + // Set of supported configuration options for a PowerShell InitialSessionState. +#if UNIX + private static readonly HashSet SupportedConfigOptions = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "AliasDefinitions", + "AssembliesToLoad", + "Author", + "CompanyName", + "Copyright", + "Description", + "EnvironmentVariables", + "FormatsToProcess", + "FunctionDefinitions", + "GUID", + "LanguageMode", + "ModulesToImport", + "MountUserDrive", + "SchemaVersion", + "ScriptsToProcess", + "SessionType", + "TranscriptDirectory", + "TypesToProcess", + "UserDriveMaximumSize", + "VisibleAliases", + "VisibleCmdlets", + "VariableDefinitions", + "VisibleExternalCommands", + "VisibleFunctions", + "VisibleProviders" + }; +#else + private static readonly HashSet SupportedConfigOptions = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "AliasDefinitions", + "AssembliesToLoad", + "Author", + "CompanyName", + "Copyright", + "Description", + "EnvironmentVariables", + "ExecutionPolicy", + "FormatsToProcess", + "FunctionDefinitions", + "GUID", + "LanguageMode", + "ModulesToImport", + "MountUserDrive", + "SchemaVersion", + "ScriptsToProcess", + "SessionType", + "TranscriptDirectory", + "TypesToProcess", + "UserDriveMaximumSize", + "VisibleAliases", + "VisibleCmdlets", + "VariableDefinitions", + "VisibleExternalCommands", + "VisibleFunctions", + "VisibleProviders" + }; +#endif + + // These are configuration options for WSMan (WinRM) endpoint configurations, that + // appearand in .pssc files, but are not part of PowerShell InitialSessionState. + private static readonly HashSet UnsupportedConfigOptions = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "GroupManagedServiceAccount", + "PowerShellVersion", + "RequiredGroups", + "RoleDefinitions", + "RunAsVirtualAccount", + "RunAsVirtualAccountGroups" + }; + + internal static void ValidateContents(Hashtable configHash) + { + foreach (var key in configHash.Keys) + { + if (key is not string keyName) + { + throw new PSInvalidOperationException(RemotingErrorIdStrings.DISCInvalidConfigKeyType); + } + + if (UnsupportedConfigOptions.Contains(keyName)) + { + throw new PSInvalidOperationException( + StringUtil.Format(RemotingErrorIdStrings.DISCUnsupportedConfigName, keyName)); + } + + if (!SupportedConfigOptions.Contains(keyName)) + { + throw new PSInvalidOperationException( + StringUtil.Format(RemotingErrorIdStrings.DISCUnknownConfigName, keyName)); + } + } + } + } + + #endregion + + #endregion } diff --git a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs index 75f70941856..e270a3efeb2 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs @@ -412,6 +412,19 @@ internal class OutOfProcessTextWriter private readonly TextWriter _writer; private bool _isStopped; private readonly object _syncObject = new object(); + private const string _errorPrepend = "__NamedPipeError__:"; + + #endregion + + #region Properties + + /// + /// Prefix for transport error message. + /// + public static string ErrorPrefix + { + get => _errorPrepend; + } #endregion @@ -421,9 +434,13 @@ internal class OutOfProcessTextWriter /// Constructs the wrapper. /// /// - internal OutOfProcessTextWriter(TextWriter writerToWrap) + public OutOfProcessTextWriter(TextWriter writerToWrap) { - Dbg.Assert(writerToWrap != null, "Cannot wrap a null writer."); + if (writerToWrap is null) + { + throw new PSArgumentNullException(nameof(writerToWrap)); + } + _writer = writerToWrap; } @@ -435,7 +452,7 @@ internal OutOfProcessTextWriter(TextWriter writerToWrap) /// Calls writer.WriteLine() with data. /// /// - internal virtual void WriteLine(string data) + public virtual void WriteLine(string data) { if (_isStopped) { @@ -467,7 +484,10 @@ internal void StopWriting() namespace System.Management.Automation.Remoting.Client { - internal abstract class OutOfProcessClientSessionTransportManagerBase : BaseClientSessionTransportManager + /// + /// Client session transport manager abstract base class. + /// + public abstract class ClientSessionTransportManagerBase : BaseClientSessionTransportManager { #region Data @@ -477,15 +497,17 @@ internal abstract class OutOfProcessClientSessionTransportManagerBase : BaseClie private OutOfProcessUtils.DataProcessingDelegates _dataProcessingCallbacks; private readonly Dictionary _cmdTransportManagers; private readonly Timer _closeTimeOutTimer; - - protected OutOfProcessTextWriter stdInWriter; - protected PowerShellTraceSource _tracer; + internal PowerShellTraceSource _tracer; + internal OutOfProcessTextWriter _messageWriter; #endregion #region Constructor - internal OutOfProcessClientSessionTransportManagerBase( + /// + /// Constructor. + /// + protected ClientSessionTransportManagerBase( Guid runspaceId, PSRemotingCryptoHelper cryptoHelper) : base(runspaceId, cryptoHelper) @@ -505,7 +527,7 @@ internal OutOfProcessClientSessionTransportManagerBase( _dataProcessingCallbacks.ClosePacketReceived += new OutOfProcessUtils.ClosePacketReceived(OnClosePacketReceived); _dataProcessingCallbacks.CloseAckPacketReceived += new OutOfProcessUtils.CloseAckPacketReceived(OnCloseAckReceived); - dataToBeSent.Fragmentor = base.Fragmentor; + dataToBeSent.Fragmentor = Fragmentor; // session transport manager can receive unlimited data..however each object is limited // by maxRecvdObjectSize. this is to allow clients to use a session for an unlimited time.. // also the messages that can be sent to a session are limited and very controlled. @@ -546,7 +568,7 @@ internal override void ConnectAsync() /// /// Closes the server process. /// - internal override void CloseAsync() + public override void CloseAsync() { bool shouldRaiseCloseCompleted = false; lock (syncObject) @@ -560,7 +582,7 @@ internal override void CloseAsync() // will know that we are closing. isClosed = true; - if (stdInWriter == null) + if (_messageWriter == null) { // this will happen if CloseAsync() is called // before ConnectAsync()..in which case we @@ -586,7 +608,7 @@ internal override void CloseAsync() try { // send Close signal to the server and let it die gracefully. - stdInWriter.WriteLine(OutOfProcessUtils.CreateClosePacket(Guid.Empty)); + _messageWriter.WriteLine(OutOfProcessUtils.CreateClosePacket(Guid.Empty)); // start the timer..so client can fail deterministically _closeTimeOutTimer.Change(60 * 1000, Timeout.Infinite); @@ -618,7 +640,7 @@ internal override BaseClientCommandTransportManager CreateClientCommandTransport Dbg.Assert(cmd != null, "Cmd cannot be null"); OutOfProcessClientCommandTransportManager result = new - OutOfProcessClientCommandTransportManager(cmd, noInput, this, stdInWriter); + OutOfProcessClientCommandTransportManager(cmd, noInput, this, _messageWriter); AddCommandTransportManager(cmd.InstanceId, result); return result; @@ -628,7 +650,7 @@ internal override BaseClientCommandTransportManager CreateClientCommandTransport /// Terminates the server process and disposes other resources. /// /// - internal override void Dispose(bool isDisposing) + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); if (isDisposing) @@ -693,6 +715,9 @@ private void OnCloseSessionCompleted() CleanupConnection(); } + /// + /// Optional additional connection clean up after a connection is closed. + /// protected abstract void CleanupConnection(); private void ProcessMessageProc(object state) @@ -731,6 +756,9 @@ private void ProcessMessageProc(object state) private const string SESSIONDMESSAGETAG = "PSGuid='00000000-0000-0000-0000-000000000000'"; + /// + /// Handles protocol output data from a transport. + /// protected void HandleOutputDataReceived(string data) { if (string.IsNullOrEmpty(data)) @@ -762,6 +790,9 @@ protected void HandleOutputDataReceived(string data) } } + /// + /// Handles protocol error data. + /// protected void HandleErrorDataReceived(string data) { lock (syncObject) @@ -778,57 +809,13 @@ protected void HandleErrorDataReceived(string data) RaiseErrorHandler(new TransportErrorOccuredEventArgs(psrte, TransportMethodEnum.Unknown)); } - protected void OnExited(object sender, EventArgs e) - { - TransportMethodEnum transportMethod = TransportMethodEnum.Unknown; - lock (syncObject) - { - // There is no need to return when IsClosed==true here as in a legitimate case process exits - // after Close is called..In that legitimate case, Exit handler is removed before - // calling Exit..So, this Exit must have been called abnormally. - if (isClosed) - { - transportMethod = TransportMethodEnum.CloseShellOperationEx; - } - - // dont let the writer write new data as the process is exited. - // Not assigning null to stdInWriter to fix the race condition between OnExited() and CloseAsync() methods. - // - stdInWriter.StopWriting(); - } - - // Try to get details about why the process exited - // and if they're not available, give information as to why - string processDiagnosticMessage; - try - { - var jobProcess = (Process)sender; - processDiagnosticMessage = StringUtil.Format( - RemotingErrorIdStrings.ProcessExitInfo, - jobProcess.ExitCode, - jobProcess.StandardOutput.ReadToEnd(), - jobProcess.StandardError.ReadToEnd()); - } - catch (Exception exception) - { - processDiagnosticMessage = StringUtil.Format( - RemotingErrorIdStrings.ProcessInfoNotRecoverable, - exception.Message); - } - - string exitErrorMsg = StringUtil.Format( - RemotingErrorIdStrings.IPCServerProcessExited, - processDiagnosticMessage); - var psrte = new PSRemotingTransportException( - PSRemotingErrorId.IPCServerProcessExited, - exitErrorMsg); - RaiseErrorHandler(new TransportErrorOccuredEventArgs(psrte, transportMethod)); - } - #endregion #region Sending Data related Methods + /// + /// Send any data packet in the queue. + /// protected void SendOneItem() { DataPriorityType priorityType; @@ -865,7 +852,7 @@ private void SendData(byte[] data, DataPriorityType priorityType) return; } - stdInWriter.WriteLine(OutOfProcessUtils.CreateDataPacket(data, + _messageWriter.WriteLine(OutOfProcessUtils.CreateDataPacket(data, priorityType, Guid.Empty)); } @@ -908,13 +895,11 @@ private void OnDataPacketReceived(byte[] rawData, string stream, Guid psGuid) { // this is for a command OutOfProcessClientCommandTransportManager cmdTM = GetCommandTransportManager(psGuid); - if (cmdTM != null) - { - // not throwing the exception in null case as the command might have already - // closed. The RS data structure handler does not wait for the close ack before - // it clears the command transport manager..so this might happen. - cmdTM.OnRemoteCmdDataReceived(rawData, streamTemp); - } + + // not throwing the exception in null case as the command might have already + // closed. The RS data structure handler does not wait for the close ack before + // it clears the command transport manager..so this might happen. + cmdTM?.OnRemoteCmdDataReceived(rawData, streamTemp); } } @@ -929,13 +914,11 @@ private void OnDataAckPacketReceived(Guid psGuid) { // this is for a command OutOfProcessClientCommandTransportManager cmdTM = GetCommandTransportManager(psGuid); - if (cmdTM != null) - { - // not throwing the exception in null case as the command might have already - // closed. The RS data structure handler does not wait for the close ack before - // it clears the command transport manager..so this might happen. - cmdTM.OnRemoteCmdSendCompleted(); - } + + // not throwing the exception in null case as the command might have already + // closed. The RS data structure handler does not wait for the close ack before + // it clears the command transport manager..so this might happen. + cmdTM?.OnRemoteCmdSendCompleted(); } } @@ -963,7 +946,8 @@ private void OnCommandCreationAckReceived(Guid psGuid) private void OnSignalPacketReceived(Guid psGuid) { - throw new PSRemotingTransportException(PSRemotingErrorId.IPCUnknownElementReceived, + throw new PSRemotingTransportException( + PSRemotingErrorId.IPCUnknownElementReceived, RemotingErrorIdStrings.IPCUnknownElementReceived, OutOfProcessUtils.PS_OUT_OF_PROC_SIGNAL_TAG); } @@ -972,17 +956,15 @@ private void OnSignalAckPacketReceived(Guid psGuid) { if (psGuid == Guid.Empty) { - throw new PSRemotingTransportException(PSRemotingErrorId.IPCNoSignalForSession, + throw new PSRemotingTransportException( + PSRemotingErrorId.IPCNoSignalForSession, RemotingErrorIdStrings.IPCNoSignalForSession, OutOfProcessUtils.PS_OUT_OF_PROC_SIGNAL_ACK_TAG); } else { OutOfProcessClientCommandTransportManager cmdTM = GetCommandTransportManager(psGuid); - if (cmdTM != null) - { - cmdTM.OnRemoteCmdSignalCompleted(); - } + cmdTM?.OnRemoteCmdSignalCompleted(); } } @@ -1012,12 +994,10 @@ private void OnCloseAckReceived(Guid psGuid) _tracer.WriteMessage("OutOfProcessClientSessionTransportManager.OnCloseAckReceived, in progress command count should be greater than zero: " + commandCount + ", RunSpacePool Id : " + this.RunspacePoolInstanceId + ", psGuid : " + psGuid.ToString()); OutOfProcessClientCommandTransportManager cmdTM = GetCommandTransportManager(psGuid); - if (cmdTM != null) - { - // this might legitimately happen if cmd is already closed before we get an - // ACK back from server. - cmdTM.OnCloseCmdCompleted(); - } + + // this might legitimately happen if cmd is already closed before we get an + // ACK back from server. + cmdTM?.OnCloseCmdCompleted(); } } @@ -1035,6 +1015,37 @@ internal void OnCloseTimeOutTimerElapsed(object source) #region Protected Methods + /// + /// Standard handler for data received, to be used by custom transport implementations. + /// + /// Protocol text data received by custom transport. + protected void HandleDataReceived(string data) + { + if (data.StartsWith(OutOfProcessTextWriter.ErrorPrefix, StringComparison.OrdinalIgnoreCase)) + { + // Error message from the server. + string errorData = data.Substring(OutOfProcessTextWriter.ErrorPrefix.Length); + HandleErrorDataReceived(errorData); + } + else + { + // Normal output data. + HandleOutputDataReceived(data); + } + } + + /// + /// Creates the transport message writer from the provided TexWriter object. + /// + /// TextWriter object to be used in the message writer. + protected void SetMessageWriter(TextWriter textWriter) + { + _messageWriter = new OutOfProcessTextWriter(textWriter); + } + + /// + /// Disposes message queue components. + /// protected void DisposeMessageQueue() { // Stop session processing thread. @@ -1065,7 +1076,7 @@ protected void DisposeMessageQueue() #endregion } - internal class OutOfProcessClientSessionTransportManager : OutOfProcessClientSessionTransportManagerBase + internal class OutOfProcessClientSessionTransportManager : ClientSessionTransportManagerBase { #region Private Data @@ -1093,14 +1104,14 @@ internal OutOfProcessClientSessionTransportManager(Guid runspaceId, /// /// Launch a new Process (pwsh -s) to perform remoting. This is used by *-Job cmdlets /// to support background jobs without depending on WinRM (WinRM has complex requirements like - /// elevation to support local machine remoting) + /// elevation to support local machine remoting). /// /// /// /// /// 1. There was an error in opening the associated file. /// - internal override void CreateAsync() + public override void CreateAsync() { if (_connectionInfo != null) { @@ -1141,8 +1152,8 @@ internal override void CreateAsync() _processInstance.Start(); StartRedirectionReaderThreads(_serverProcess); - stdInWriter = new OutOfProcessTextWriter(_serverProcess.StandardInput); - _processInstance.StdInWriter = stdInWriter; + SetMessageWriter(_serverProcess.StandardInput); + _processInstance.StdInWriter = _messageWriter; } } catch (System.ComponentModel.Win32Exception w32e) @@ -1252,7 +1263,7 @@ private void ProcessErrorData(object arg) /// Kills the server process and disposes other resources. /// /// - internal override void Dispose(bool isDisposing) + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); if (isDisposing) @@ -1316,10 +1327,57 @@ private void KillServerProcess() } } + private void OnExited(object sender, EventArgs e) + { + TransportMethodEnum transportMethod = TransportMethodEnum.Unknown; + lock (syncObject) + { + // There is no need to return when IsClosed==true here as in a legitimate case process exits + // after Close is called..In that legitimate case, Exit handler is removed before + // calling Exit..So, this Exit must have been called abnormally. + if (isClosed) + { + transportMethod = TransportMethodEnum.CloseShellOperationEx; + } + + // dont let the writer write new data as the process is exited. + // Not assigning null to stdInWriter to fix the race condition between OnExited() and CloseAsync() methods. + // + _messageWriter.StopWriting(); + } + + // Try to get details about why the process exited + // and if they're not available, give information as to why + string processDiagnosticMessage; + try + { + var jobProcess = (Process)sender; + processDiagnosticMessage = StringUtil.Format( + RemotingErrorIdStrings.ProcessExitInfo, + jobProcess.ExitCode, + jobProcess.StandardOutput.ReadToEnd(), + jobProcess.StandardError.ReadToEnd()); + } + catch (Exception exception) + { + processDiagnosticMessage = StringUtil.Format( + RemotingErrorIdStrings.ProcessInfoNotRecoverable, + exception.Message); + } + + string exitErrorMsg = StringUtil.Format( + RemotingErrorIdStrings.IPCServerProcessExited, + processDiagnosticMessage); + var psrte = new PSRemotingTransportException( + PSRemotingErrorId.IPCServerProcessExited, + exitErrorMsg); + RaiseErrorHandler(new TransportErrorOccuredEventArgs(psrte, transportMethod)); + } + #endregion } - internal abstract class HyperVSocketClientSessionTransportManagerBase : OutOfProcessClientSessionTransportManagerBase + internal abstract class HyperVSocketClientSessionTransportManagerBase : ClientSessionTransportManagerBase { #region Data @@ -1341,16 +1399,13 @@ internal HyperVSocketClientSessionTransportManagerBase( #region Overrides - internal override void Dispose(bool isDisposing) + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); if (isDisposing) { - if (_client != null) - { - _client.Dispose(); - } + _client?.Dispose(); } } @@ -1485,7 +1540,7 @@ internal VMHyperVSocketClientSessionTransportManager( /// Create a Hyper-V socket connection to the target process and set up /// transport reader/writer. /// - internal override void CreateAsync() + public override void CreateAsync() { _client = new RemoteSessionHyperVSocketClient(_vmGuid, true); if (!_client.Connect(_networkCredential, _configurationName, true)) @@ -1514,7 +1569,7 @@ internal override void CreateAsync() } // Create writer for Hyper-V socket. - stdInWriter = new OutOfProcessTextWriter(_client.TextWriter); + SetMessageWriter(_client.TextWriter); // Create reader thread for Hyper-V socket. StartReaderThread(_client.TextReader); @@ -1558,7 +1613,7 @@ internal ContainerHyperVSocketClientSessionTransportManager( /// Create a Hyper-V socket connection to the target process and set up /// transport reader/writer. /// - internal override void CreateAsync() + public override void CreateAsync() { _client = new RemoteSessionHyperVSocketClient(_targetGuid, false, true); if (!_client.Connect(null, string.Empty, false)) @@ -1573,7 +1628,7 @@ internal override void CreateAsync() } // Create writer for Hyper-V socket. - stdInWriter = new OutOfProcessTextWriter(_client.TextWriter); + SetMessageWriter(_client.TextWriter); // Create reader thread for Hyper-V socket. StartReaderThread(_client.TextReader); @@ -1582,7 +1637,7 @@ internal override void CreateAsync() #endregion } - internal sealed class SSHClientSessionTransportManager : OutOfProcessClientSessionTransportManagerBase + internal sealed class SSHClientSessionTransportManager : ClientSessionTransportManagerBase { #region Data @@ -1615,7 +1670,7 @@ internal SSHClientSessionTransportManager( #region Overrides - internal override void Dispose(bool isDisposing) + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -1634,7 +1689,7 @@ protected override void CleanupConnection() /// Create an SSH connection to the target host and set up /// transport reader/writer. /// - internal override void CreateAsync() + public override void CreateAsync() { // Create the ssh client process with connection to host target. _sshProcessId = _connectionInfo.StartSSHProcess( @@ -1646,7 +1701,7 @@ internal override void CreateAsync() StartErrorThread(_stdErrReader); // Create writer for named pipe. - stdInWriter = new OutOfProcessTextWriter(_stdInWriter); + SetMessageWriter(_stdInWriter); // Create reader thread and send first PSRP message. StartReaderThread(_stdOutReader); @@ -1695,7 +1750,7 @@ internal override void CreateAsync() period: Timeout.Infinite); } - internal override void CloseAsync() + public override void CloseAsync() { base.CloseAsync(); @@ -1762,7 +1817,16 @@ private void ProcessErrorThread(object state) while (true) { - string error = ReadError(reader); + string error; + + // Blocking read from StdError stream + error = reader.ReadLine(); + + if (error == null) + { + // Stream is closed unexpectedly. + throw new PSInvalidOperationException(RemotingErrorIdStrings.SSHAbruptlyTerminated); + } if (error.Length == 0) { @@ -1770,12 +1834,15 @@ private void ProcessErrorThread(object state) continue; } - // Any SSH client error results in a broken session. - PSRemotingTransportException psrte = new PSRemotingTransportException( - PSRemotingErrorId.IPCServerProcessReportedError, - RemotingErrorIdStrings.IPCServerProcessReportedError, - StringUtil.Format(RemotingErrorIdStrings.SSHClientEndWithErrorMessage, error)); - HandleSSHError(psrte); + try + { + // Messages in error stream from ssh are unreliable, and may just be warnings or + // banner text. + // So just report the messages but don't act on them. + System.Console.WriteLine(error); + } + catch (IOException) + { } } } catch (ObjectDisposedException) @@ -1801,55 +1868,6 @@ private void HandleSSHError(PSRemotingTransportException psrte) CloseConnection(); } - private static string ReadError(StreamReader reader) - { - // Blocking read from StdError stream - string error = reader.ReadLine(); - - if (error == null) - { - // Stream is closed unexpectedly. - throw new PSInvalidOperationException(RemotingErrorIdStrings.SSHAbruptlyTerminated); - } - - if ((error.Length == 0) || - error.Contains("WARNING:", StringComparison.OrdinalIgnoreCase)) - { - // Handle as interactive warning message - Console.WriteLine(error); - return string.Empty; - } - - // SSH may return a multi-line error message. - // The StdError pipe stream is open ended causing StreamReader read operations to block - // if there is no incoming data. Since we don't know how many error message lines there - // will be we use an asynchronous read with timeout to prevent blocking indefinitely. - System.Text.StringBuilder sb = new Text.StringBuilder(error); - var running = true; - while (running) - { - try - { - var task = reader.ReadLineAsync(); - if (task.Wait(1000) && (task.Result != null)) - { - sb.Append(Environment.NewLine); - sb.Append(task.Result); - } - else - { - running = false; - } - } - catch (Exception) - { - running = false; - } - } - - return sb.ToString(); - } - private void StartReaderThread( StreamReader reader) { @@ -1881,10 +1899,10 @@ private void ProcessReaderThread(object state) break; } - if (data.StartsWith(System.Management.Automation.Remoting.Server.NamedPipeErrorTextWriter.ErrorPrepend, StringComparison.OrdinalIgnoreCase)) + if (data.StartsWith(System.Management.Automation.Remoting.Server.FormattedErrorTextWriter.ErrorPrefix, StringComparison.OrdinalIgnoreCase)) { // Error message from the server. - string errorData = data.Substring(System.Management.Automation.Remoting.Server.NamedPipeErrorTextWriter.ErrorPrepend.Length); + string errorData = data.Substring(System.Management.Automation.Remoting.Server.FormattedErrorTextWriter.ErrorPrefix.Length); HandleErrorDataReceived(errorData); } else @@ -1905,7 +1923,7 @@ private void ProcessReaderThread(object state) { if (e is ArgumentOutOfRangeException) { - Dbg.Assert(false, "Need to adjust transport fragmentor to accomodate read buffer size."); + Dbg.Assert(false, "Need to adjust transport fragmentor to accommodate read buffer size."); } string errorMsg = e.Message ?? string.Empty; @@ -1917,7 +1935,7 @@ private void ProcessReaderThread(object state) #endregion } - internal abstract class NamedPipeClientSessionTransportManagerBase : OutOfProcessClientSessionTransportManagerBase + internal abstract class NamedPipeClientSessionTransportManagerBase : ClientSessionTransportManagerBase { #region Data @@ -1950,16 +1968,13 @@ internal NamedPipeClientSessionTransportManagerBase( #region Overrides - internal override void Dispose(bool isDisposing) + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); if (isDisposing) { - if (_clientPipe != null) - { - _clientPipe.Dispose(); - } + _clientPipe?.Dispose(); } } @@ -2011,17 +2026,7 @@ private void ProcessReaderThread(object state) break; } - if (data.StartsWith(System.Management.Automation.Remoting.Server.NamedPipeErrorTextWriter.ErrorPrepend, StringComparison.OrdinalIgnoreCase)) - { - // Error message from the server. - string errorData = data.Substring(System.Management.Automation.Remoting.Server.NamedPipeErrorTextWriter.ErrorPrepend.Length); - HandleErrorDataReceived(errorData); - } - else - { - // Normal output data. - HandleOutputDataReceived(data); - } + HandleDataReceived(data); } } catch (ObjectDisposedException) @@ -2078,7 +2083,7 @@ internal NamedPipeClientSessionTransportManager( /// Create a named pipe connection to the target process and set up /// transport reader/writer. /// - internal override void CreateAsync() + public override void CreateAsync() { _clientPipe = string.IsNullOrEmpty(_connectionInfo.CustomPipeName) ? new RemoteSessionNamedPipeClient(_connectionInfo.ProcessId, _connectionInfo.AppDomainName) : @@ -2088,7 +2093,7 @@ internal override void CreateAsync() _clientPipe.Connect(_connectionInfo.OpenTimeout); // Create writer for named pipe. - stdInWriter = new OutOfProcessTextWriter(_clientPipe.TextWriter); + SetMessageWriter(_clientPipe.TextWriter); // Create reader thread for named pipe. StartReaderThread(_clientPipe.TextReader); @@ -2101,13 +2106,7 @@ internal override void CreateAsync() /// /// Aborts an existing connection attempt. /// - public void AbortConnect() - { - if (_clientPipe != null) - { - _clientPipe.AbortConnect(); - } - } + public void AbortConnect() => _clientPipe?.AbortConnect(); #endregion } @@ -2146,7 +2145,7 @@ internal ContainerNamedPipeClientSessionTransportManager( /// Create a named pipe connection to the target process in target container and set up /// transport reader/writer. /// - internal override void CreateAsync() + public override void CreateAsync() { _clientPipe = new ContainerSessionNamedPipeClient( _connectionInfo.ContainerProc.ProcessId, @@ -2157,7 +2156,7 @@ internal override void CreateAsync() _clientPipe.Connect(_connectionInfo.OpenTimeout); // Create writer for named pipe. - stdInWriter = new OutOfProcessTextWriter(_clientPipe.TextWriter); + SetMessageWriter(_clientPipe.TextWriter); // Create reader thread for named pipe. StartReaderThread(_clientPipe.TextReader); @@ -2196,7 +2195,7 @@ internal class OutOfProcessClientCommandTransportManager : BaseClientCommandTran internal OutOfProcessClientCommandTransportManager( ClientRemotePowerShell cmd, bool noInput, - OutOfProcessClientSessionTransportManagerBase sessnTM, + ClientSessionTransportManagerBase sessnTM, OutOfProcessTextWriter stdInWriter) : base(cmd, sessnTM.CryptoHelper, sessnTM) { _stdInWriter = stdInWriter; @@ -2214,7 +2213,7 @@ internal override void ConnectAsync() throw new NotImplementedException(RemotingErrorIdStrings.IPCTransportConnectError); } - internal override void CreateAsync() + public override void CreateAsync() { PSEtwLog.LogAnalyticInformational(PSEventId.WSManCreateCommand, PSOpcode.Connect, PSTask.CreateRunspace, @@ -2224,7 +2223,7 @@ internal override void CreateAsync() _stdInWriter.WriteLine(OutOfProcessUtils.CreateCommandPacket(powershellInstanceId)); } - internal override void CloseAsync() + public override void CloseAsync() { lock (syncObject) { @@ -2278,7 +2277,7 @@ internal override void SendStopSignal() _signalTimeOutTimer.Change(60 * 1000, Timeout.Infinite); } - internal override void Dispose(bool isDisposing) + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); if (isDisposing) @@ -2503,6 +2502,12 @@ internal OutOfProcessServerSessionTransportManager(OutOfProcessTextWriter outWri _stdOutWriter = outWriter; _stdErrWriter = errWriter; _cmdTransportManagers = new Dictionary(); + + this.WSManTransportErrorOccured += (object sender, TransportErrorOccuredEventArgs e) => + { + string msg = e.Exception.TransportMessage ?? e.Exception.InnerException?.Message ?? string.Empty; + _stdErrWriter.WriteLine(StringUtil.Format(RemotingErrorIdStrings.RemoteTransportError, msg)); + }; } #endregion diff --git a/src/System.Management.Automation/engine/remoting/fanin/PSPrincipal.cs b/src/System.Management.Automation/engine/remoting/fanin/PSPrincipal.cs index 1341c9e0a83..67a5ff4e80d 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/PSPrincipal.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/PSPrincipal.cs @@ -18,9 +18,8 @@ namespace System.Management.Automation.Remoting /// /// This class is used in the server side remoting scenarios. This class /// holds information about the incoming connection like: - /// (a) Client's TimeZone - /// (b) Connecting User information - /// (c) Connection String used by the user to connect to the server. + /// (a) Connecting User information + /// (b) Connection String used by the user to connect to the server. /// [Serializable] public sealed class PSSenderInfo : ISerializable @@ -81,8 +80,6 @@ private PSSenderInfo(SerializationInfo info, StreamingContext context) UserInfo = senderInfo.UserInfo; ConnectionString = senderInfo.ConnectionString; _applicationArguments = senderInfo._applicationArguments; - - ClientTimeZone = senderInfo.ClientTimeZone; } catch (Exception) { @@ -129,11 +126,7 @@ public PSPrincipal UserInfo /// /// Contains the TimeZone information from the client machine. /// - public TimeZoneInfo ClientTimeZone - { - get; - internal set; - } + public TimeZoneInfo ClientTimeZone => null; /// /// Connection string used by the client to connect to the server. This is diff --git a/src/System.Management.Automation/engine/remoting/fanin/PSSessionConfigurationData.cs b/src/System.Management.Automation/engine/remoting/fanin/PSSessionConfigurationData.cs index 6da557cca40..fbd2653c8a7 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/PSSessionConfigurationData.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/PSSessionConfigurationData.cs @@ -224,8 +224,8 @@ private void Update(string optionName, string optionValue) private void CreateCollectionIfNecessary() { - if (_modulesToImport == null) _modulesToImport = new List(); - if (_modulesToImportInternal == null) _modulesToImportInternal = new List(); + _modulesToImport ??= new List(); + _modulesToImportInternal ??= new List(); } private const string SessionConfigToken = "SessionConfigurationData"; diff --git a/src/System.Management.Automation/engine/remoting/fanin/PriorityCollection.cs b/src/System.Management.Automation/engine/remoting/fanin/PriorityCollection.cs index 7baebdc98ae..ce9b2fe09cb 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/PriorityCollection.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/PriorityCollection.cs @@ -220,21 +220,29 @@ internal byte[] ReadOrRegisterCallback(OnDataAvailableCallback callback, lock (_readSyncObject) { priorityType = DataPriorityType.Default; - - // send data from which ever stream that has data directly. + // Send data from which ever stream that has data directly. byte[] result = null; - result = _dataToBeSent[(int)DataPriorityType.PromptResponse].ReadOrRegisterCallback(_onSendCollectionDataAvailable); - priorityType = DataPriorityType.PromptResponse; + SerializedDataStream promptDataToBeSent = _dataToBeSent[(int)DataPriorityType.PromptResponse]; + if (promptDataToBeSent is not null) + { + result = promptDataToBeSent.ReadOrRegisterCallback(_onSendCollectionDataAvailable); + priorityType = DataPriorityType.PromptResponse; + } if (result == null) { - result = _dataToBeSent[(int)DataPriorityType.Default].ReadOrRegisterCallback(_onSendCollectionDataAvailable); - priorityType = DataPriorityType.Default; + SerializedDataStream defaultDataToBeSent = _dataToBeSent[(int)DataPriorityType.Default]; + if (defaultDataToBeSent is not null) + { + result = defaultDataToBeSent.ReadOrRegisterCallback(_onSendCollectionDataAvailable); + priorityType = DataPriorityType.Default; + } } - // no data to return..so register the callback. + + // No data to return..so register the callback. if (result == null) { - // register callback. + // Register callback. _onDataAvailableCallback = callback; } @@ -676,10 +684,7 @@ internal void ProcessRawData(byte[] data, OnDataAvailableCallback callback) private void ResetReceiveData() { // reset resources used to store incoming data (for a single object) - if (_dataToProcessStream != null) - { - _dataToProcessStream.Dispose(); - } + _dataToProcessStream?.Dispose(); _currentObjectId = 0; _currentFrgId = 0; diff --git a/src/System.Management.Automation/engine/remoting/fanin/WSManPlugin.cs b/src/System.Management.Automation/engine/remoting/fanin/WSManPlugin.cs index 9382beb9b31..79d583e063f 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/WSManPlugin.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/WSManPlugin.cs @@ -306,10 +306,16 @@ internal void CreateShell( PSOpcode.Connect, PSTask.None, PSKeyword.ManagedPlugin | PSKeyword.UseAlwaysAnalytic, requestDetails.ToString(), senderInfo.UserInfo.Identity.Name, requestDetails.resourceUri); - ServerRemoteSession remoteShellSession = ServerRemoteSession.CreateServerRemoteSession(senderInfo, - requestDetails.resourceUri, - extraInfo, - serverTransportMgr); + + ServerRemoteSession remoteShellSession = ServerRemoteSession.CreateServerRemoteSession( + senderInfo: senderInfo, + configurationProviderId: requestDetails.resourceUri, + initializationParameters: extraInfo, + transportManager: serverTransportMgr, + initialCommand: null, // Not used by WinRM endpoint. + configurationName: null, // Not used by WinRM endpoint, which has its own configuration. + configurationFile: null, // Same. + initialLocation: null); // Same. if (remoteShellSession == null) { diff --git a/src/System.Management.Automation/engine/remoting/fanin/WSManPluginFacade.cs b/src/System.Management.Automation/engine/remoting/fanin/WSManPluginFacade.cs index f01797fd760..7fbc236a4dd 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/WSManPluginFacade.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/WSManPluginFacade.cs @@ -446,11 +446,7 @@ public static void ShutdownPlugin( IntPtr pluginContext) { WSManPluginInstance.PerformShutdown(pluginContext); - - if (workerPtrs != null) - { - workerPtrs.Dispose(); - } + workerPtrs?.Dispose(); } /// diff --git a/src/System.Management.Automation/engine/remoting/fanin/WSManTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/WSManTransportManager.cs index 60e4228c9a3..bc8244673b2 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/WSManTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/WSManTransportManager.cs @@ -1034,7 +1034,7 @@ internal override void StartReceivingData() /// /// WSManCreateShellEx failed. /// - internal override void CreateAsync() + public override void CreateAsync() { Dbg.Assert(!isClosed, "object already disposed"); Dbg.Assert(!string.IsNullOrEmpty(ConnectionInfo.ShellUri), "shell uri cannot be null or empty."); @@ -1189,7 +1189,7 @@ internal override void CreateAsync() /// Closes the pending Create,Send,Receive operations and then closes the shell and release all the resources. /// The caller should make sure this method is called only after calling ConnectAsync. /// - internal override void CloseAsync() + public override void CloseAsync() { bool shouldRaiseCloseCompleted = false; // let other threads release the lock before we clean up the resources. @@ -1492,20 +1492,9 @@ private void Initialize(Uri connectionUri, WSManConnectionInfo connectionInfo) finally { // release resources - if (proxyAuthCredentials != null) - { - proxyAuthCredentials.Dispose(); - } - - if (proxyInfo != null) - { - proxyInfo.Dispose(); - } - - if (authCredentials != null) - { - authCredentials.Dispose(); - } + proxyAuthCredentials?.Dispose(); + proxyInfo?.Dispose(); + authCredentials?.Dispose(); } if (result != 0) @@ -1640,7 +1629,7 @@ internal void ProcessWSManTransportError(TransportErrorOccuredEventArgs eventArg /// Log the error message in the Crimson logger and raise error handler. /// /// - internal override void RaiseErrorHandler(TransportErrorOccuredEventArgs eventArgs) + public override void RaiseErrorHandler(TransportErrorOccuredEventArgs eventArgs) { // Look for a valid stack trace. string stackTrace; @@ -2543,7 +2532,7 @@ private void SendData(byte[] data, DataPriorityType priorityType) #region Dispose / Destructor pattern [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed")] - internal override void Dispose(bool isDisposing) + protected override void Dispose(bool isDisposing) { tracer.WriteLine("Disposing session with session context: {0} Operation Context: {1}", _sessionContextID, _wsManShellOperationHandle); @@ -3013,11 +3002,12 @@ internal override void ConnectAsync() } /// + /// Begin connection creation. /// /// /// WSManRunShellCommandEx failed. /// - internal override void CreateAsync() + public override void CreateAsync() { byte[] cmdPart1 = serializedPipeline.ReadOrRegisterCallback(null); if (cmdPart1 != null) @@ -3146,7 +3136,7 @@ internal override void SendStopSignal() /// /// Closes the pending Create,Send,Receive operations and then closes the shell and release all the resources. /// - internal override void CloseAsync() + public override void CloseAsync() { tracer.WriteLine("Closing command with command context: {0} Operation Context {1}", _cmdContextId, _wsManCmdOperationHandle); @@ -3213,7 +3203,7 @@ internal void ProcessWSManTransportError(TransportErrorOccuredEventArgs eventArg /// Log the error message in the Crimson logger and raise error handler. /// /// - internal override void RaiseErrorHandler(TransportErrorOccuredEventArgs eventArgs) + public override void RaiseErrorHandler(TransportErrorOccuredEventArgs eventArgs) { // Look for a valid stack trace. string stackTrace; @@ -4086,7 +4076,7 @@ internal override void StartReceivingData() #region Dispose / Destructor pattern - internal override void Dispose(bool isDisposing) + protected override void Dispose(bool isDisposing) { tracer.WriteLine("Disposing command with command context: {0} Operation Context: {1}", _cmdContextId, _wsManCmdOperationHandle); base.Dispose(isDisposing); diff --git a/src/System.Management.Automation/engine/remoting/server/OutOfProcServerMediator.cs b/src/System.Management.Automation/engine/remoting/server/OutOfProcServerMediator.cs index fe17a31f0cb..14b0240858b 100644 --- a/src/System.Management.Automation/engine/remoting/server/OutOfProcServerMediator.cs +++ b/src/System.Management.Automation/engine/remoting/server/OutOfProcServerMediator.cs @@ -200,10 +200,7 @@ protected void OnSignalPacketReceived(Guid psGuid) } // dont throw if there is no cmdTM as it might have legitimately closed - if (cmdTM != null) - { - cmdTM.Close(null); - } + cmdTM?.Close(null); } finally { @@ -244,12 +241,9 @@ protected void OnClosePacketReceived(Guid psGuid) { tracer.WriteMessage("OnClosePacketReceived, in progress commands count should be zero : " + _inProgressCommandsCount + ", psGuid : " + psGuid.ToString()); - if (sessionTM != null) - { - // it appears that when closing PowerShell ISE, therefore closing OutOfProcServerMediator, there are 2 Close command requests - // changing PSRP/IPC at this point is too risky, therefore protecting about this duplication - sessionTM.Close(null); - } + // it appears that when closing PowerShell ISE, therefore closing OutOfProcServerMediator, there are 2 Close command requests + // changing PSRP/IPC at this point is too risky, therefore protecting about this duplication + sessionTM?.Close(null); tracer.WriteMessage("END calling close on session transport manager"); sessionTM = null; @@ -268,10 +262,7 @@ protected void OnClosePacketReceived(Guid psGuid) } // dont throw if there is no cmdTM as it might have legitimately closed - if (cmdTM != null) - { - cmdTM.Close(null); - } + cmdTM?.Close(null); lock (_syncObject) { @@ -300,6 +291,7 @@ protected void OnCloseAckPacketReceived(Guid psGuid) protected OutOfProcessServerSessionTransportManager CreateSessionTransportManager( string configurationName, + string configurationFile, PSRemotingCryptoHelperServer cryptoHelper, string workingDirectory) { @@ -317,14 +309,20 @@ protected OutOfProcessServerSessionTransportManager CreateSessionTransportManage senderInfo = new PSSenderInfo(userPrincipal, "http://localhost"); #endif - OutOfProcessServerSessionTransportManager tm = new OutOfProcessServerSessionTransportManager(originalStdOut, originalStdErr, cryptoHelper); + var tm = new OutOfProcessServerSessionTransportManager( + originalStdOut, + originalStdErr, + cryptoHelper); ServerRemoteSession.CreateServerRemoteSession( - senderInfo, - _initialCommand, - tm, - configurationName, - workingDirectory); + senderInfo: senderInfo, + configurationProviderId: "Microsoft.PowerShell", + initializationParameters: string.Empty, + transportManager: tm, + initialCommand: _initialCommand, + configurationName: configurationName, + configurationFile: configurationFile, + initialLocation: workingDirectory); return tm; } @@ -333,11 +331,16 @@ protected void Start( string initialCommand, PSRemotingCryptoHelperServer cryptoHelper, string workingDirectory, - string configurationName) + string configurationName, + string configurationFile) { _initialCommand = initialCommand; - sessionTM = CreateSessionTransportManager(configurationName, cryptoHelper, workingDirectory); + sessionTM = CreateSessionTransportManager( + configurationName: configurationName, + configurationFile: configurationFile, + cryptoHelper: cryptoHelper, + workingDirectory: workingDirectory); try { @@ -346,10 +349,11 @@ protected void Start( string data = originalStdIn.ReadLine(); lock (_syncObject) { - if (sessionTM == null) - { - sessionTM = CreateSessionTransportManager(configurationName, cryptoHelper, workingDirectory); - } + sessionTM ??= CreateSessionTransportManager( + configurationName: configurationName, + configurationFile: configurationFile, + cryptoHelper: cryptoHelper, + workingDirectory: workingDirectory); } if (string.IsNullOrEmpty(data)) @@ -432,7 +436,8 @@ internal sealed class StdIOProcessMediator : OutOfProcessMediatorBase /// It will replace StdIn,StdOut and StdErr stream with TextWriter.Null. This is /// to make sure these streams are totally used by our Mediator. /// - private StdIOProcessMediator() : base(true) + /// Redirects remoting errors to the Out stream. + private StdIOProcessMediator(bool combineErrOutStream) : base(exitProcessOnError: true) { // Create input stream reader from Console standard input stream. // We don't use the provided Console.In TextReader because it can have @@ -441,16 +446,23 @@ private StdIOProcessMediator() : base(true) // stream encoding. This way the stream encoding is determined by the // stream BOM as needed. originalStdIn = new StreamReader(Console.OpenStandardInput(), true); - Console.SetIn(TextReader.Null); - // replacing StdOut with Null so that no other app messes with the - // original stream + // Remoting errors can optionally be written to stdErr or stdOut with + // special formatting. originalStdOut = new OutOfProcessTextWriter(Console.Out); - Console.SetOut(TextWriter.Null); + if (combineErrOutStream) + { + originalStdErr = new FormattedErrorTextWriter(Console.Out); + } + else + { + originalStdErr = new OutOfProcessTextWriter(Console.Error); + } - // replacing StdErr with Null so that no other app messes with the - // original stream - originalStdErr = new OutOfProcessTextWriter(Console.Error); + // Replacing StdIn, StdOut, StdErr with Null so that no other app messes with the + // original streams. + Console.SetIn(TextReader.Null); + Console.SetOut(TextWriter.Null); Console.SetError(TextWriter.Null); } @@ -464,10 +476,14 @@ private StdIOProcessMediator() : base(true) /// Specifies the initialization script. /// Specifies the initial working directory. The working directory is set before the initial command. /// Specifies an optional configuration name that configures the endpoint session. + /// Specifies an optional path to a configuration (.pssc) file for the session. + /// Specifies the option to write remoting errors to stdOut stream, with special formatting. internal static void Run( string initialCommand, string workingDirectory, - string configurationName) + string configurationName, + string configurationFile, + bool combineErrOutStream) { lock (SyncObject) { @@ -477,14 +493,15 @@ internal static void Run( return; } - s_singletonInstance = new StdIOProcessMediator(); + s_singletonInstance = new StdIOProcessMediator(combineErrOutStream); } s_singletonInstance.Start( initialCommand: initialCommand, cryptoHelper: new PSRemotingCryptoHelperServer(), workingDirectory: workingDirectory, - configurationName: configurationName); + configurationName: configurationName, + configurationFile: configurationFile); } #endregion @@ -526,7 +543,7 @@ private NamedPipeProcessMediator( // Create transport reader/writers from named pipe. originalStdIn = namedPipeServer.TextReader; originalStdOut = new OutOfProcessTextWriter(namedPipeServer.TextWriter); - originalStdErr = new NamedPipeErrorTextWriter(namedPipeServer.TextWriter); + originalStdErr = new FormattedErrorTextWriter(namedPipeServer.TextWriter); #if !UNIX // Flow impersonation as needed. @@ -557,32 +574,18 @@ internal static void Run( initialCommand: initialCommand, cryptoHelper: new PSRemotingCryptoHelperServer(), workingDirectory: null, - configurationName: namedPipeServer.ConfigurationName); + configurationName: namedPipeServer.ConfigurationName, + configurationFile: null); } #endregion } - internal sealed class NamedPipeErrorTextWriter : OutOfProcessTextWriter + internal sealed class FormattedErrorTextWriter : OutOfProcessTextWriter { - #region Private Members - - private const string _errorPrepend = "__NamedPipeError__:"; - - #endregion - - #region Properties - - internal static string ErrorPrepend - { - get { return _errorPrepend; } - } - - #endregion - #region Constructors - internal NamedPipeErrorTextWriter( + internal FormattedErrorTextWriter( TextWriter textWriter) : base(textWriter) { } @@ -590,9 +593,11 @@ internal NamedPipeErrorTextWriter( #region Base class overrides - internal override void WriteLine(string data) + // Write error data to stream with 'ErrorPrefix' prefix that will + // be interpreted by the client. + public override void WriteLine(string data) { - string dataToWrite = (data != null) ? _errorPrepend + data : null; + string dataToWrite = (data != null) ? ErrorPrefix + data : null; base.WriteLine(dataToWrite); } @@ -647,7 +652,8 @@ internal static void Run( initialCommand: initialCommand, cryptoHelper: new PSRemotingCryptoHelperServer(), workingDirectory: null, - configurationName: configurationName); + configurationName: configurationName, + configurationFile: null); } #endregion @@ -681,7 +687,7 @@ internal HyperVSocketErrorTextWriter( #region Base class overrides - internal override void WriteLine(string data) + public override void WriteLine(string data) { string dataToWrite = (data != null) ? _errorPrepend + data : null; base.WriteLine(dataToWrite); diff --git a/src/System.Management.Automation/engine/remoting/server/ServerPowerShellDriver.cs b/src/System.Management.Automation/engine/remoting/server/ServerPowerShellDriver.cs index 6a49d6f3a4a..871f4f21c4b 100644 --- a/src/System.Management.Automation/engine/remoting/server/ServerPowerShellDriver.cs +++ b/src/System.Management.Automation/engine/remoting/server/ServerPowerShellDriver.cs @@ -798,11 +798,9 @@ private void HandleSessionConnected(object sender, EventArgs eventArgs) { // Close input if its active. no need to synchronize as input stream would have already been processed // when connect call came into PS plugin - if (InputCollection != null) - { - // TODO: Post an ETW event - InputCollection.Complete(); - } + + // TODO: Post an ETW event + InputCollection?.Complete(); } /// diff --git a/src/System.Management.Automation/engine/remoting/server/ServerRemoteHost.cs b/src/System.Management.Automation/engine/remoting/server/ServerRemoteHost.cs index ca7ff805c02..eaed49ef40a 100644 --- a/src/System.Management.Automation/engine/remoting/server/ServerRemoteHost.cs +++ b/src/System.Management.Automation/engine/remoting/server/ServerRemoteHost.cs @@ -352,10 +352,7 @@ public override void PopRunspace() { if (_pushedRunspace != null) { - if (_debugger != null) - { - _debugger.PopDebugger(); - } + _debugger?.PopDebugger(); if (_hostSupportsPSEdit) { diff --git a/src/System.Management.Automation/engine/remoting/server/ServerRemotingProtocol2.cs b/src/System.Management.Automation/engine/remoting/server/ServerRemotingProtocol2.cs index f87f89b5bac..f45a67583a9 100644 --- a/src/System.Management.Automation/engine/remoting/server/ServerRemotingProtocol2.cs +++ b/src/System.Management.Automation/engine/remoting/server/ServerRemotingProtocol2.cs @@ -266,10 +266,7 @@ internal void DispatchMessageToPowerShell(RemoteDataObject rcvdData) // if data structure handler is not found, then association has already been // removed, discard message - if (dsHandler != null) - { - dsHandler.ProcessReceivedData(rcvdData); - } + dsHandler?.ProcessReceivedData(rcvdData); } /// diff --git a/src/System.Management.Automation/engine/remoting/server/ServerRunspacePoolDriver.cs b/src/System.Management.Automation/engine/remoting/server/ServerRunspacePoolDriver.cs index 69866f2d4ee..11c2d6c4d24 100644 --- a/src/System.Management.Automation/engine/remoting/server/ServerRunspacePoolDriver.cs +++ b/src/System.Management.Automation/engine/remoting/server/ServerRunspacePoolDriver.cs @@ -271,10 +271,7 @@ internal void Start() internal void SendApplicationPrivateDataToClient() { // Include Debug mode information. - if (_applicationPrivateData == null) - { - _applicationPrivateData = new PSPrimitiveDictionary(); - } + _applicationPrivateData ??= new PSPrimitiveDictionary(); if (_serverRemoteDebugger != null) { @@ -350,10 +347,7 @@ internal void Close() { Runspace runspaceToDispose = _remoteHost.PushedRunspace; _remoteHost.PopRunspace(); - if (runspaceToDispose != null) - { - runspaceToDispose.Dispose(); - } + runspaceToDispose?.Dispose(); } DisposeRemoteDebugger(); @@ -486,13 +480,7 @@ private void SetupRemoteDebugger(Runspace runspace) } } - private void DisposeRemoteDebugger() - { - if (_serverRemoteDebugger != null) - { - _serverRemoteDebugger.Dispose(); - } - } + private void DisposeRemoteDebugger() => _serverRemoteDebugger?.Dispose(); /// /// Invokes a script. @@ -812,10 +800,7 @@ private void HandleCreateAndInvokePowerShell(object _, RemoteDataEventArgs @@ -2232,15 +2208,8 @@ public void Dispose() ExitDebugMode(DebuggerResumeAction.Stop); } - if (_nestedDebugStopCompleteEvent != null) - { - _nestedDebugStopCompleteEvent.Dispose(); - } - - if (_processCommandCompleteEvent != null) - { - _processCommandCompleteEvent.Dispose(); - } + _nestedDebugStopCompleteEvent?.Dispose(); + _processCommandCompleteEvent?.Dispose(); } #endregion @@ -2312,10 +2281,7 @@ public DebuggerCommandResults Invoke(ManualResetEventSlim startInvokeEvent) public void Stop() { Debugger debugger = _wrappedDebugger; - if (debugger != null) - { - debugger.StopProcessCommand(); - } + debugger?.StopProcessCommand(); } internal void DoInvoke() @@ -2527,10 +2493,7 @@ private void EnterDebugMode(bool isNestedStop) { // Blocking call for nested debugger execution (Debug-Runspace) stop events. // The root debugger never makes two EnterDebugMode calls without an ExitDebugMode. - if (_nestedDebugStopCompleteEvent == null) - { - _nestedDebugStopCompleteEvent = new ManualResetEventSlim(false); - } + _nestedDebugStopCompleteEvent ??= new ManualResetEventSlim(false); _nestedDebugging = true; OnEnterDebugMode(_nestedDebugStopCompleteEvent); diff --git a/src/System.Management.Automation/engine/remoting/server/ServerSteppablePipelineDriver.cs b/src/System.Management.Automation/engine/remoting/server/ServerSteppablePipelineDriver.cs index 57df27e2d4b..3a9388625e6 100644 --- a/src/System.Management.Automation/engine/remoting/server/ServerSteppablePipelineDriver.cs +++ b/src/System.Management.Automation/engine/remoting/server/ServerSteppablePipelineDriver.cs @@ -241,10 +241,7 @@ internal void Start() _eventSubscriber.FireStartSteppablePipeline(this); - if (_powershellInput != null) - { - _powershellInput.Pulse(); - } + _powershellInput?.Pulse(); } #endregion Internal Methods @@ -262,20 +259,14 @@ internal void HandleInputEndReceived(object sender, EventArgs eventArgs) CheckAndPulseForProcessing(true); - if (_powershellInput != null) - { - _powershellInput.Pulse(); - } + _powershellInput?.Pulse(); } private void HandleSessionConnected(object sender, EventArgs eventArgs) { // Close input if its active. no need to synchronize as input stream would have already been processed // when connect call came into PS plugin - if (Input != null) - { - Input.Complete(); - } + Input?.Complete(); } /// @@ -302,10 +293,7 @@ private void HandleStopReceived(object sender, EventArgs eventArgs) PerformStop(); - if (_powershellInput != null) - { - _powershellInput.Pulse(); - } + _powershellInput?.Pulse(); } /// @@ -326,10 +314,7 @@ private void HandleInputReceived(object sender, RemoteDataEventArgs even CheckAndPulseForProcessing(false); - if (_powershellInput != null) - { - _powershellInput.Pulse(); - } + _powershellInput?.Pulse(); } } diff --git a/src/System.Management.Automation/engine/remoting/server/ServerSteppablePipelineSubscriber.cs b/src/System.Management.Automation/engine/remoting/server/ServerSteppablePipelineSubscriber.cs index 328571647cb..cce8b0bbcd3 100644 --- a/src/System.Management.Automation/engine/remoting/server/ServerSteppablePipelineSubscriber.cs +++ b/src/System.Management.Automation/engine/remoting/server/ServerSteppablePipelineSubscriber.cs @@ -162,7 +162,7 @@ private void HandleProcessRecord(object sender, PSEventArgs args) if (!driver.NoInput || isProcessCalled) { // if there is noInput then we - // need to call process atleast once + // need to call process at least once break; } } @@ -274,11 +274,8 @@ internal void FireStartSteppablePipeline(ServerSteppablePipelineDriver driver) { lock (_syncObject) { - if (_eventManager != null) - { - _eventManager.GenerateEvent(_startSubscriber.SourceIdentifier, this, - new object[1] { new ServerSteppablePipelineDriverEventArg(driver) }, null, true, false); - } + _eventManager?.GenerateEvent(_startSubscriber.SourceIdentifier, this, + new object[1] { new ServerSteppablePipelineDriverEventArg(driver) }, null, true, false); } } @@ -290,11 +287,8 @@ internal void FireHandleProcessRecord(ServerSteppablePipelineDriver driver) { lock (_syncObject) { - if (_eventManager != null) - { - _eventManager.GenerateEvent(_processSubscriber.SourceIdentifier, this, - new object[1] { new ServerSteppablePipelineDriverEventArg(driver) }, null, true, false); - } + _eventManager?.GenerateEvent(_processSubscriber.SourceIdentifier, this, + new object[1] { new ServerSteppablePipelineDriverEventArg(driver) }, null, true, false); } } diff --git a/src/System.Management.Automation/engine/remoting/server/serverremotesession.cs b/src/System.Management.Automation/engine/remoting/server/serverremotesession.cs index 7b08c674301..7f0e5636a4e 100644 --- a/src/System.Management.Automation/engine/remoting/server/serverremotesession.cs +++ b/src/System.Management.Automation/engine/remoting/server/serverremotesession.cs @@ -89,6 +89,10 @@ internal class ServerRemoteSession : RemoteSession // Creates a pushed remote runspace session created with this configuration name. private string _configurationName; + // Specifies an optional .pssc configuration file path for out-of-proc session use. + // The .pssc file is used to configure the runspace for the endpoint session. + private string _configurationFile; + // Specifies an initial location of the powershell session. private string _initialLocation; @@ -173,7 +177,9 @@ internal ServerRemoteSession(PSSenderInfo senderInfo, /// xml. /// /// + /// Optional initial command used for OutOfProc sessions. /// Optional configuration endpoint name for OutOfProc sessions. + /// Optional configuration file (.pssc) path for OutOfProc sessions. /// Optional configuration initial location of the powershell session. /// /// @@ -192,8 +198,10 @@ internal static ServerRemoteSession CreateServerRemoteSession( string configurationProviderId, string initializationParameters, AbstractServerSessionTransportManager transportManager, - string configurationName = null, - string initialLocation = null) + string initialCommand, + string configurationName, + string configurationFile, + string initialLocation) { Dbg.Assert( (senderInfo != null) && (senderInfo.UserInfo != null), @@ -215,7 +223,9 @@ internal static ServerRemoteSession CreateServerRemoteSession( initializationParameters, transportManager) { + _initScriptForOutOfProcRS = initialCommand, _configurationName = configurationName, + _configurationFile = configurationFile, _initialLocation = initialLocation }; @@ -226,33 +236,6 @@ internal static ServerRemoteSession CreateServerRemoteSession( return result; } - /// - /// Used by OutOfProcessServerMediator to create a remote session. - /// - /// - /// - /// - /// - /// - /// - internal static ServerRemoteSession CreateServerRemoteSession( - PSSenderInfo senderInfo, - string initializationScriptForOutOfProcessRunspace, - AbstractServerSessionTransportManager transportManager, - string configurationName, - string initialLocation) - { - ServerRemoteSession result = CreateServerRemoteSession( - senderInfo, - "Microsoft.PowerShell", - string.Empty, - transportManager, - configurationName: configurationName, - initialLocation: initialLocation); - result._initScriptForOutOfProcRS = initializationScriptForOutOfProcessRunspace; - return result; - } - #endregion #region Overrides @@ -723,13 +706,7 @@ internal void ExecuteConnect(byte[] connectData, out byte[] connectResponseData) } // pass on application private data when session is connected from new client - internal void HandlePostConnect() - { - if (_runspacePoolDriver != null) - { - _runspacePoolDriver.SendApplicationPrivateDataToClient(); - } - } + internal void HandlePostConnect() => _runspacePoolDriver?.SendApplicationPrivateDataToClient(); /// /// @@ -749,20 +726,12 @@ private void HandleCreateRunspacePool(object sender, RemoteDataEventArgs createR RemoteDataObject rcvdData = createRunspaceEventArg.ReceivedData; Dbg.Assert(rcvdData != null, "rcvdData must be non-null"); - // set the PSSenderInfo sent in the first packets - // This is used by the initial session state configuration providers like Exchange. - if (Context != null) - { - _senderInfo.ClientTimeZone = Context.ClientCapability.TimeZone; - } - _senderInfo.ApplicationArguments = RemotingDecoder.GetApplicationArguments(rcvdData.Data); // Get Initial Session State from custom session config suppliers // like Exchange. ConfigurationDataFromXML configurationData = - PSSessionConfiguration.LoadEndPointConfiguration(_configProviderId, - _initParameters); + PSSessionConfiguration.LoadEndPointConfiguration(_configProviderId, _initParameters); // used by Out-Of-Proc (IPC) runspace. configurationData.InitializationScriptForOutOfProcessRunspace = _initScriptForOutOfProcRS; // start with data from configuration XML and then override with data @@ -770,8 +739,6 @@ private void HandleCreateRunspacePool(object sender, RemoteDataEventArgs createR _maxRecvdObjectSize = configurationData.MaxReceivedObjectSizeMB; _maxRecvdDataSizeCommand = configurationData.MaxReceivedCommandSizeMB; - DISCPowerShellConfiguration discProvider = null; - if (string.IsNullOrEmpty(configurationData.ConfigFilePath)) { _sessionConfigProvider = configurationData.CreateEndPointConfigurationInstance(); @@ -779,11 +746,8 @@ private void HandleCreateRunspacePool(object sender, RemoteDataEventArgs createR else { System.Security.Principal.WindowsPrincipal windowsPrincipal = new System.Security.Principal.WindowsPrincipal(_senderInfo.UserInfo.WindowsIdentity); - Func validator = (role) => windowsPrincipal.IsInRole(role); - - discProvider = new DISCPowerShellConfiguration(configurationData.ConfigFilePath, validator); - _sessionConfigProvider = discProvider; + _sessionConfigProvider = new DISCPowerShellConfiguration(configurationData.ConfigFilePath, validator); } // exchange of ApplicationArguments and ApplicationPrivateData is be done as early as possible @@ -794,6 +758,7 @@ private void HandleCreateRunspacePool(object sender, RemoteDataEventArgs createR if (configurationData.SessionConfigurationData != null) { + // Use the provided WinRM endpoint runspace configuration information. try { rsSessionStateToUse = @@ -804,8 +769,21 @@ private void HandleCreateRunspacePool(object sender, RemoteDataEventArgs createR rsSessionStateToUse = _sessionConfigProvider.GetInitialSessionState(_senderInfo); } } + else if (!string.IsNullOrEmpty(_configurationFile)) + { + // Use the optional _configurationFile parameter to create the endpoint runspace configuration. + // This parameter is only used by Out-Of-Proc transports (not WinRM transports). + var discConfiguration = new Remoting.DISCPowerShellConfiguration( + configFile: _configurationFile, + roleVerifier: null, + validateFile: true); + rsSessionStateToUse = discConfiguration.GetInitialSessionState(_senderInfo); + } else { + // Create a runspace configuration based on the provided PSSessionConfiguration provider. + // This can be either a 'default' configuration, or third party configuration PSSessionConfiguration provider object. + // So far, only Exchange provides a custom PSSessionConfiguration provider implementation. rsSessionStateToUse = _sessionConfigProvider.GetInitialSessionState(_senderInfo); } @@ -824,32 +802,14 @@ private void HandleCreateRunspacePool(object sender, RemoteDataEventArgs createR RemotingErrorIdStrings.PSSenderInfoDescription), ScopedItemOptions.ReadOnly)); - // check if the current scenario is Win7(client) to Win8(server). Add back the PSv2 version TabExpansion - // function if necessary. + // Get client PS version from PSSenderInfo. Version psClientVersion = null; if (_senderInfo.ApplicationArguments != null && _senderInfo.ApplicationArguments.ContainsKey("PSversionTable")) { var value = PSObject.Base(_senderInfo.ApplicationArguments["PSversionTable"]) as PSPrimitiveDictionary; - if (value != null) + if (value != null && value.ContainsKey("PSVersion")) { - if (value.ContainsKey("WSManStackVersion")) - { - var wsmanStackVersion = PSObject.Base(value["WSManStackVersion"]) as Version; - if (wsmanStackVersion != null && wsmanStackVersion.Major < 3) - { - // The client side is PSv2. This is the Win7 to Win8 scenario. We need to add the PSv2 - // TabExpansion function back in to keep the tab expansion functionable on the client side. - rsSessionStateToUse.Commands.Add( - new SessionStateFunctionEntry( - RemoteDataNameStrings.PSv2TabExpansionFunction, - RemoteDataNameStrings.PSv2TabExpansionFunctionText)); - } - } - - if (value.ContainsKey("PSVersion")) - { - psClientVersion = PSObject.Base(value["PSVersion"]) as Version; - } + psClientVersion = PSObject.Base(value["PSVersion"]) as Version; } } @@ -907,7 +867,7 @@ private void HandleCreateRunspacePool(object sender, RemoteDataEventArgs createR } /// - /// This handler method runs the negotiation algorithm. It decides if the negotiation is succesful, + /// This handler method runs the negotiation algorithm. It decides if the negotiation is successful, /// or fails. /// /// @@ -961,10 +921,7 @@ private void HandleNegotiationReceived(object sender, RemoteSessionNegotiationEv /// private void HandleSessionDSHandlerClosing(object sender, EventArgs eventArgs) { - if (_runspacePoolDriver != null) - { - _runspacePoolDriver.Close(); - } + _runspacePoolDriver?.Close(); // dispose the session configuration object..this will let them // clean their resources. diff --git a/src/System.Management.Automation/engine/remoting/server/serverremotesessionstatemachine.cs b/src/System.Management.Automation/engine/remoting/server/serverremotesessionstatemachine.cs index 732e5a836ab..3a6bb4c1f53 100644 --- a/src/System.Management.Automation/engine/remoting/server/serverremotesessionstatemachine.cs +++ b/src/System.Management.Automation/engine/remoting/server/serverremotesessionstatemachine.cs @@ -913,10 +913,7 @@ private void DoKeyExchange(object sender, RemoteSessionStateMachineEventArgs eve { // reset the timer Timer tmp = Interlocked.Exchange(ref _keyExchangeTimer, null); - if (tmp != null) - { - tmp.Dispose(); - } + tmp?.Dispose(); } // the key import would have been done @@ -984,10 +981,7 @@ private void HandleKeyExchangeTimeout(object sender) Dbg.Assert(_state == RemoteSessionState.EstablishedAndKeyRequested, "timeout should only happen when waiting for a key"); Timer tmp = Interlocked.Exchange(ref _keyExchangeTimer, null); - if (tmp != null) - { - tmp.Dispose(); - } + tmp?.Dispose(); PSRemotingDataStructureException exception = new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerKeyExchangeFailed); diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index a4917d5af6b..8662cc72ecd 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -2089,9 +2089,7 @@ internal static void NoteTypeHasInstanceMemberOrTypeName(Type type) internal static object CopyInstanceMembersOfValueType(T t, object boxedT) where T : struct { - PSMemberInfoInternalCollection unused1; - ConsolidatedString unused2; - if (PSObject.HasInstanceMembers(boxedT, out unused1) || PSObject.HasInstanceTypeName(boxedT, out unused2)) + if (PSObject.HasInstanceMembers(boxedT, out _) || PSObject.HasInstanceTypeName(boxedT, out _)) { var psobj = PSObject.AsPSObject(boxedT); return PSObject.Base(psobj.Copy()); @@ -5277,7 +5275,17 @@ public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, Dy return GenerateGetPropertyException(restrictions).WriteToDebugLog(this); } - expr = Expression.Property(targetExpr, propertyAccessor); + if (propertyAccessor.PropertyType.IsByRef) + { + expr = Expression.Call( + CachedReflectionInfo.ByRefOps_GetByRefPropertyValue, + targetExpr, + Expression.Constant(propertyAccessor)); + } + else + { + expr = Expression.Property(targetExpr, propertyAccessor); + } } else { @@ -5335,13 +5343,9 @@ public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, Dy if (!isGeneric || genericTypeArg != null) { var temp = Expression.Variable(typeof(object)); - if (expr == null) - { - // If expr is not null, it's the fallback when no member exists. If it is null, - // the fallback is the result from PropertyDoesntExist. - - expr = (errorSuggestion ?? PropertyDoesntExist(target, restrictions)).Expression; - } + // If expr is not null, it's the fallback when no member exists. If it is null, + // the fallback is the result from PropertyDoesntExist. + expr ??= (errorSuggestion ?? PropertyDoesntExist(target, restrictions)).Expression; var method = isGeneric ? CachedReflectionInfo.PSGetMemberBinder_TryGetGenericDictionaryValue.MakeGenericMethod(genericTypeArg) @@ -5613,8 +5617,7 @@ internal PSMemberInfo GetPSMemberInfo(DynamicMetaObject target, canOptimize = false; - PSMemberInfo unused; - Diagnostics.Assert(!TryGetInstanceMember(target.Value, Name, out unused), + Diagnostics.Assert(!TryGetInstanceMember(target.Value, Name, out _), "shouldn't get here if there is an instance member"); PSMemberInfo memberInfo = null; @@ -5675,19 +5678,13 @@ internal PSMemberInfo GetPSMemberInfo(DynamicMetaObject target, restrictions = versionRestriction; // When returning aliasRestrictions always include the version restriction - if (aliasRestrictions != null) - { - aliasRestrictions.Add(versionRestriction); - } + aliasRestrictions?.Add(versionRestriction); var alias = memberInfo as PSAliasProperty; if (alias != null) { aliasConversionType = alias.ConversionType; - if (aliasRestrictions == null) - { - aliasRestrictions = new List(); - } + aliasRestrictions ??= new List(); memberInfo = ResolveAlias(alias, target, aliases, aliasRestrictions); if (memberInfo == null) @@ -5738,10 +5735,7 @@ internal PSMemberInfo GetPSMemberInfo(DynamicMetaObject target, var methodInfo = member as MethodInfo; if (methodInfo != null && (methodInfo.IsPublic || methodInfo.IsFamily)) { - if (candidateMethods == null) - { - candidateMethods = new List(); - } + candidateMethods ??= new List(); candidateMethods.Add(methodInfo); } @@ -5834,10 +5828,7 @@ internal static object GetAdaptedValue(object obj, string member) } var adapterSet = PSObject.GetMappedAdapter(obj, context?.TypeTable); - if (memberInfo == null) - { - memberInfo = adapterSet.OriginalAdapter.BaseGetMember(obj, member); - } + memberInfo ??= adapterSet.OriginalAdapter.BaseGetMember(obj, member); if (memberInfo == null && adapterSet.DotNetAdapter != null) { @@ -6438,10 +6429,7 @@ internal static object SetAdaptedValue(object obj, string member, object value) } var adapterSet = PSObject.GetMappedAdapter(obj, context?.TypeTable); - if (memberInfo == null) - { - memberInfo = adapterSet.OriginalAdapter.BaseGetMember(obj, member); - } + memberInfo ??= adapterSet.OriginalAdapter.BaseGetMember(obj, member); if (memberInfo == null && adapterSet.DotNetAdapter != null) { @@ -6903,6 +6891,20 @@ internal static DynamicMetaObject InvokeDotNetMethod( expr = Expression.Block(expr, ExpressionCache.AutomationNullConstant); } + // Expression block runs two expressions in order: + // - Log method invocation to AMSI Notifications (can throw PSSecurityException) + // - Invoke method + string targetName = methodInfo.ReflectedType?.FullName ?? string.Empty; + expr = Expression.Block( + Expression.Call( + CachedReflectionInfo.MemberInvocationLoggingOps_LogMemberInvocation, + Expression.Constant(targetName), + Expression.Constant(name), + Expression.NewArrayInit( + typeof(object), + args.Select(static e => e.Expression.Cast(typeof(object))))), + expr); + // If we're calling SteppablePipeline.{Begin|Process|End}, we don't want // to wrap exceptions - this is very much a special case to help error // propagation and ensure errors are attributed to the correct code (the @@ -7236,14 +7238,11 @@ private DynamicMetaObject InvokeMemberOnCollection(DynamicMetaObject targetEnume private static DynamicMetaObject GetTargetAsEnumerable(DynamicMetaObject target) { var enumerableTarget = PSEnumerableBinder.IsEnumerable(target); - if (enumerableTarget == null) - { - // Wrap the target in an array. - enumerableTarget = PSEnumerableBinder.IsEnumerable( - new DynamicMetaObject( - Expression.NewArrayInit(typeof(object), target.Expression.Cast(typeof(object))), - target.GetSimpleTypeRestriction())); - } + // If null wrap the target in an array. + enumerableTarget ??= PSEnumerableBinder.IsEnumerable( + new DynamicMetaObject( + Expression.NewArrayInit(typeof(object), target.Expression.Cast(typeof(object))), + target.GetSimpleTypeRestriction())); return enumerableTarget; } @@ -7427,7 +7426,20 @@ internal static object InvokeAdaptedMember(object obj, string methodName, object if (string.Equals(methodName, "Foreach", StringComparison.OrdinalIgnoreCase)) { var enumerator = (new object[] { obj }).GetEnumerator(); - return EnumerableOps.ForEach(enumerator, args[0], Array.Empty()); + object[] argsToPass; + + if (args.Length > 1) + { + int length = args.Length - 1; + argsToPass = new object[length]; + Array.Copy(args, sourceIndex: 1, argsToPass, destinationIndex: 0, length: length); + } + else + { + argsToPass = Array.Empty(); + } + + return EnumerableOps.ForEach(enumerator, args[0], argsToPass); } throw InterpreterError.NewInterpreterException(methodName, typeof(RuntimeException), null, diff --git a/src/System.Management.Automation/engine/runtime/CompiledScriptBlock.cs b/src/System.Management.Automation/engine/runtime/CompiledScriptBlock.cs index 2e6db041126..9feeab94364 100644 --- a/src/System.Management.Automation/engine/runtime/CompiledScriptBlock.cs +++ b/src/System.Management.Automation/engine/runtime/CompiledScriptBlock.cs @@ -12,11 +12,9 @@ using System.Management.Automation.Runspaces; using System.Management.Automation.Tracing; using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Security.Cryptography.X509Certificates; using System.Text; -using System.Threading.Tasks; #if LEGACYTELEMETRY using Microsoft.PowerShell.Telemetry.Internal; #endif @@ -35,6 +33,7 @@ internal enum ScriptBlockClauseToInvoke Begin, Process, End, + Clean, ProcessBlockOnly, } @@ -247,6 +246,7 @@ bool IsScriptBlockInFactASafeHashtable() if (scriptBlockAst.BeginBlock != null || scriptBlockAst.ProcessBlock != null + || scriptBlockAst.CleanBlock != null || scriptBlockAst.ParamBlock != null || scriptBlockAst.DynamicParamBlock != null || scriptBlockAst.ScriptRequirements != null @@ -316,6 +316,8 @@ private IParameterMetadataProvider DelayParseScriptText() internal Dictionary NameToIndexMap { get; set; } + #region Named Blocks + internal Action DynamicParamBlock { get; set; } internal Action UnoptimizedDynamicParamBlock { get; set; } @@ -332,6 +334,12 @@ private IParameterMetadataProvider DelayParseScriptText() internal Action UnoptimizedEndBlock { get; set; } + internal Action CleanBlock { get; set; } + + internal Action UnoptimizedCleanBlock { get; set; } + + #endregion Named Blocks + internal IScriptExtent[] SequencePoints { get; set; } private RuntimeDefinedParameterDictionary _runtimeDefinedParameterDictionary; @@ -358,10 +366,7 @@ internal bool IsProductCode { get { - if (_isProductCode == null) - { - _isProductCode = SecuritySupport.IsProductBinary(((Ast)_ast).Extent.File); - } + _isProductCode ??= SecuritySupport.IsProductBinary(((Ast)_ast).Extent.File); return _isProductCode.Value; } @@ -753,7 +758,7 @@ private PipelineAst GetSimplePipeline(Func errorHandler) { errorHandler ??= (static _ => null); - if (HasBeginBlock || HasProcessBlock) + if (HasBeginBlock || HasProcessBlock || HasCleanBlock) { return errorHandler(AutomationExceptions.CanConvertOneClauseOnly); } @@ -891,7 +896,10 @@ public void CheckRestrictedLanguage( Parser parser = new Parser(); var ast = AstInternal; - if (HasBeginBlock || HasProcessBlock || ast.Body.ParamBlock != null) + if (HasBeginBlock + || HasProcessBlock + || HasCleanBlock + || ast.Body.ParamBlock is not null) { Ast errorAst = ast.Body.BeginBlock ?? (Ast)ast.Body.ProcessBlock ?? ast.Body.ParamBlock; parser.ReportError( @@ -974,6 +982,11 @@ internal void InvokeWithPipeImpl( InvocationInfo invocationInfo, params object[] args) { + if (clauseToInvoke == ScriptBlockClauseToInvoke.Clean) + { + throw new PSNotSupportedException(ParserStrings.InvokingCleanBlockNotSupported); + } + if ((clauseToInvoke == ScriptBlockClauseToInvoke.Begin && !HasBeginBlock) || (clauseToInvoke == ScriptBlockClauseToInvoke.Process && !HasProcessBlock) || (clauseToInvoke == ScriptBlockClauseToInvoke.End && !HasEndBlock)) @@ -991,7 +1004,7 @@ internal void InvokeWithPipeImpl( throw new PipelineStoppedException(); } - // Validate at the arguments are consistent. The only public API that gets you here never sets createLocalScope to false... + // Validate that the arguments are consistent. The only public API that gets you here never sets createLocalScope to false... Diagnostics.Assert( createLocalScope || functionsToDefine == null, "When calling ScriptBlock.InvokeWithContext(), if 'functionsToDefine' != null then 'createLocalScope' must be true"); @@ -999,10 +1012,7 @@ internal void InvokeWithPipeImpl( createLocalScope || variablesToDefine == null, "When calling ScriptBlock.InvokeWithContext(), if 'variablesToDefine' != null then 'createLocalScope' must be true"); - if (args == null) - { - args = Array.Empty(); - } + args ??= Array.Empty(); bool runOptimized = context._debuggingMode <= 0 && createLocalScope; var codeToInvoke = GetCodeToInvoke(ref runOptimized, clauseToInvoke); @@ -1011,11 +1021,8 @@ internal void InvokeWithPipeImpl( return; } - if (outputPipe == null) - { - // If we don't have a pipe to write to, we need to discard all results. - outputPipe = new Pipe { NullPipe = true }; - } + // If we don't have a pipe to write to, we need to discard all results. + outputPipe ??= new Pipe { NullPipe = true }; var locals = MakeLocalsTuple(runOptimized); @@ -1195,7 +1202,7 @@ internal void InvokeWithPipeImpl( _sequencePoints = SequencePoints, }; - ScriptBlock.LogScriptBlockStart(this, context.CurrentRunspace.InstanceId); + LogScriptBlockStart(this, context.CurrentRunspace.InstanceId); try { @@ -1203,7 +1210,7 @@ internal void InvokeWithPipeImpl( } finally { - ScriptBlock.LogScriptBlockEnd(this, context.CurrentRunspace.InstanceId); + LogScriptBlockEnd(this, context.CurrentRunspace.InstanceId); } } catch (TargetInvocationException tie) @@ -1362,7 +1369,7 @@ internal static void SetAutomaticVariable(AutomaticVariable variable, object val private Action GetCodeToInvoke(ref bool optimized, ScriptBlockClauseToInvoke clauseToInvoke) { if (clauseToInvoke == ScriptBlockClauseToInvoke.ProcessBlockOnly - && (HasBeginBlock || (HasEndBlock && HasProcessBlock))) + && (HasBeginBlock || HasCleanBlock || (HasEndBlock && HasProcessBlock))) { throw PSTraceSource.NewInvalidOperationException(AutomationExceptions.ScriptBlockInvokeOnOneClauseOnly); } @@ -1379,6 +1386,8 @@ private Action GetCodeToInvoke(ref bool optimized, ScriptBlockC return _scriptBlockData.ProcessBlock; case ScriptBlockClauseToInvoke.End: return _scriptBlockData.EndBlock; + case ScriptBlockClauseToInvoke.Clean: + return _scriptBlockData.CleanBlock; default: return HasProcessBlock ? _scriptBlockData.ProcessBlock : _scriptBlockData.EndBlock; } @@ -1392,6 +1401,8 @@ private Action GetCodeToInvoke(ref bool optimized, ScriptBlockC return _scriptBlockData.UnoptimizedProcessBlock; case ScriptBlockClauseToInvoke.End: return _scriptBlockData.UnoptimizedEndBlock; + case ScriptBlockClauseToInvoke.Clean: + return _scriptBlockData.UnoptimizedCleanBlock; default: return HasProcessBlock ? _scriptBlockData.UnoptimizedProcessBlock : _scriptBlockData.UnoptimizedEndBlock; } @@ -2022,7 +2033,7 @@ public static string Match(string text) continue; } - for (int j = Math.Min(i, runningHash.Length) - 1; j > 0; j--) + for (int j = Math.Min(i, runningHash.Length - 1); j > 0; j--) { // Say our input is: `Emit` (our shortest pattern, len 4). // Towards the end just before matching, we will: @@ -2147,11 +2158,17 @@ internal static void LogScriptBlockEnd(ScriptBlock scriptBlock, Guid runspaceId) internal Action UnoptimizedEndBlock { get => _scriptBlockData.UnoptimizedEndBlock; } + internal Action CleanBlock { get => _scriptBlockData.CleanBlock; } + + internal Action UnoptimizedCleanBlock { get => _scriptBlockData.UnoptimizedCleanBlock; } + internal bool HasBeginBlock { get => AstInternal.Body.BeginBlock != null; } internal bool HasProcessBlock { get => AstInternal.Body.ProcessBlock != null; } internal bool HasEndBlock { get => AstInternal.Body.EndBlock != null; } + + internal bool HasCleanBlock { get => AstInternal.Body.CleanBlock != null; } } [Serializable] @@ -2197,11 +2214,13 @@ internal sealed class PSScriptCmdlet : PSCmdlet, IDynamicParameters, IDisposable private readonly bool _useLocalScope; private readonly bool _runOptimized; private readonly bool _rethrowExitException; - private MshCommandRuntime _commandRuntime; private readonly MutableTuple _localsTuple; - private bool _exitWasCalled; private readonly FunctionContext _functionContext; + private MshCommandRuntime _commandRuntime; + private bool _exitWasCalled; + private bool _anyClauseExecuted; + public PSScriptCmdlet(ScriptBlock scriptBlock, bool useNewScope, bool fromScriptFile, ExecutionContext context) { _scriptBlock = scriptBlock; @@ -2291,6 +2310,34 @@ internal override void DoEndProcessing() } } + internal override void DoCleanResource() + { + if (_scriptBlock.HasCleanBlock && _anyClauseExecuted) + { + // The 'Clean' block doesn't write any output to pipeline, so we use a 'NullPipe' here and + // disallow the output to be collected by an 'out' variable. However, the error, warning, + // and information records should still be collectable by the corresponding variables. + Pipe oldOutputPipe = _commandRuntime.OutputPipe; + _functionContext._outputPipe = _commandRuntime.OutputPipe = new Pipe + { + NullPipe = true, + IgnoreOutVariableList = true, + }; + + try + { + RunClause( + clause: _runOptimized ? _scriptBlock.CleanBlock : _scriptBlock.UnoptimizedCleanBlock, + dollarUnderbar: AutomationNull.Value, + inputToProcess: AutomationNull.Value); + } + finally + { + _functionContext._outputPipe = _commandRuntime.OutputPipe = oldOutputPipe; + } + } + } + private void EnterScope() { _commandRuntime.SetVariableListsInPipe(); @@ -2303,6 +2350,7 @@ private void ExitScope() private void RunClause(Action clause, object dollarUnderbar, object inputToProcess) { + _anyClauseExecuted = true; Pipe oldErrorOutputPipe = this.Context.ShellFunctionErrorOutputPipe; // If the script block has a different language mode than the current, @@ -2356,9 +2404,9 @@ private void RunClause(Action clause, object dollarUnderbar, ob } finally { - this.Context.RestoreErrorPipe(oldErrorOutputPipe); + Context.ShellFunctionErrorOutputPipe = oldErrorOutputPipe; - // Set the language mode + // Restore the language mode if (oldLanguageMode.HasValue) { Context.LanguageMode = oldLanguageMode.Value; @@ -2518,9 +2566,6 @@ public void Dispose() commandRuntime = null; currentObjectInPipeline = null; _input.Clear(); - // _scriptBlock = null; - // _localsTuple = null; - // _functionContext = null; base.InternalDispose(true); _disposed = true; diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index de9ef697fc6..1c15c6510bc 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -13,6 +13,7 @@ using System.Management.Automation.Internal.Host; using System.Management.Automation.Language; using System.Management.Automation.Runspaces; +using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; @@ -426,10 +427,7 @@ internal static void InvokePipeline(object input, try { - if (context.Events != null) - { - context.Events.ProcessPendingActions(); - } + context.Events?.ProcessPendingActions(); if (input == AutomationNull.Value && !ignoreInput) { @@ -519,10 +517,7 @@ internal static void InvokePipelineInBackground( try { - if (context.Events != null) - { - context.Events.ProcessPendingActions(); - } + context.Events?.ProcessPendingActions(); CommandProcessorBase commandProcessor = null; @@ -531,12 +526,8 @@ internal static void InvokePipelineInBackground( var pipelineOffset = pipelineAst.Extent.StartOffset; var variables = pipelineAst.FindAll(static x => x is VariableExpressionAst, true); - // Used to make sure that the job runs in the current directory - const string cmdPrefix = @"Microsoft.PowerShell.Management\Set-Location -LiteralPath $using:pwd ; "; - - // Minimize allocations by initializing the stringbuilder to the size of the source string + prefix + space for ${using:} * 2 - System.Text.StringBuilder updatedScriptblock = new System.Text.StringBuilder(cmdPrefix.Length + scriptblockBodyString.Length + 18); - updatedScriptblock.Append(cmdPrefix); + // Minimize allocations by initializing the stringbuilder to the size of the source string + space for ${using:} * 2 + System.Text.StringBuilder updatedScriptblock = new System.Text.StringBuilder(scriptblockBodyString.Length + 18); int position = 0; // Prefix variables in the scriptblock with $using: @@ -568,14 +559,25 @@ internal static void InvokePipelineInBackground( var sb = ScriptBlock.Create(updatedScriptblock.ToString()); var commandInfo = new CmdletInfo("Start-Job", typeof(StartJobCommand)); commandProcessor = context.CommandDiscovery.LookupCommandProcessor(commandInfo, CommandOrigin.Internal, false, context.EngineSessionState); - var parameter = CommandParameterInternal.CreateParameterWithArgument( + + var workingDirectoryParameter = CommandParameterInternal.CreateParameterWithArgument( parameterAst: pipelineAst, - "ScriptBlock", - null, + parameterName: "WorkingDirectory", + parameterText: null, argumentAst: pipelineAst, - sb, - false); - commandProcessor.AddParameter(parameter); + value: context.SessionState.Path.CurrentLocation.Path, + spaceAfterParameter: false); + + var scriptBlockParameter = CommandParameterInternal.CreateParameterWithArgument( + parameterAst: pipelineAst, + parameterName: "ScriptBlock", + parameterText: null, + argumentAst: pipelineAst, + value: sb, + spaceAfterParameter: false); + + commandProcessor.AddParameter(workingDirectoryParameter); + commandProcessor.AddParameter(scriptBlockParameter); pipelineProcessor.Add(commandProcessor); pipelineProcessor.LinkPipelineSuccessOutput(outputPipe ?? new Pipe(new List())); @@ -834,10 +836,7 @@ internal static ExitException GetExitException(object exitCodeObj) internal static void CheckForInterrupts(ExecutionContext context) { - if (context.Events != null) - { - context.Events.ProcessPendingActions(); - } + context.Events?.ProcessPendingActions(); if (context.CurrentPipelineStopping) { @@ -1058,10 +1057,7 @@ internal override void Bind(PipelineProcessor pipelineProcessor, CommandProcesso // Normally, context.CurrentCommandProcessor will not be null. But in legacy DRTs from ParserTest.cs, // a scriptblock may be invoked through 'DoInvokeReturnAsIs' using .NET reflection. In that case, // context.CurrentCommandProcessor will be null. We don't try passing along variable lists in such case. - if (context.CurrentCommandProcessor != null) - { - context.CurrentCommandProcessor.CommandRuntime.OutputPipe.SetVariableListForTemporaryPipe(pipe); - } + context.CurrentCommandProcessor?.CommandRuntime.OutputPipe.SetVariableListForTemporaryPipe(pipe); commandProcessor.CommandRuntime.OutputPipe = pipe; commandProcessor.CommandRuntime.ErrorOutputPipe = pipe; @@ -1072,10 +1068,7 @@ internal override void Bind(PipelineProcessor pipelineProcessor, CommandProcesso break; case RedirectionStream.Output: // Since a temp output pipe is going to be used, we should pass along the error and warning variable list. - if (context.CurrentCommandProcessor != null) - { - context.CurrentCommandProcessor.CommandRuntime.OutputPipe.SetVariableListForTemporaryPipe(pipe); - } + context.CurrentCommandProcessor?.CommandRuntime.OutputPipe.SetVariableListForTemporaryPipe(pipe); commandProcessor.CommandRuntime.OutputPipe = pipe; break; @@ -1207,11 +1200,8 @@ internal Pipe GetRedirectionPipe(ExecutionContext context, PipelineProcessor par throw; } - if (parentPipelineProcessor != null) - { - // I think this is only necessary for calling Dispose on the commands in the redirection pipe. - parentPipelineProcessor.AddRedirectionPipe(PipelineProcessor); - } + // I think this is only necessary for calling Dispose on the commands in the redirection pipe. + parentPipelineProcessor?.AddRedirectionPipe(PipelineProcessor); return new Pipe(context, PipelineProcessor); } @@ -1227,10 +1217,7 @@ internal Pipe GetRedirectionPipe(ExecutionContext context, PipelineProcessor par internal void CallDoCompleteForExpression() { // The pipe returned from 'GetRedirectionPipe' could be a NullPipe - if (PipelineProcessor != null) - { - PipelineProcessor.DoComplete(); - } + PipelineProcessor?.DoComplete(); } private bool _disposed; @@ -1248,10 +1235,7 @@ private void Dispose(bool disposing) if (disposing) { - if (PipelineProcessor != null) - { - PipelineProcessor.Dispose(); - } + PipelineProcessor?.Dispose(); } _disposed = true; @@ -1314,6 +1298,17 @@ internal ScriptBlock GetScriptBlock(ExecutionContext context, bool isFilter) } } + internal static class ByRefOps + { + /// + /// There is no way to directly work with ByRef type in the expression tree, so we turn to reflection in this case. + /// + internal static object GetByRefPropertyValue(object target, PropertyInfo property) + { + return property.GetValue(target); + } + } + internal static class HashtableOps { internal static void AddKeyValuePair(IDictionary hashtable, object key, object value, IScriptExtent errorExtent) @@ -1578,23 +1573,33 @@ private static int FindMatchingHandlerByType(Type exceptionType, Type[] types) internal static bool SuspendStoppingPipeline(ExecutionContext context) { - LocalPipeline lpl = (LocalPipeline)context.CurrentRunspace.GetCurrentlyRunningPipeline(); - if (lpl != null) + var localPipeline = (LocalPipeline)context.CurrentRunspace.GetCurrentlyRunningPipeline(); + return SuspendStoppingPipelineImpl(localPipeline); + } + + internal static void RestoreStoppingPipeline(ExecutionContext context, bool oldIsStopping) + { + var localPipeline = (LocalPipeline)context.CurrentRunspace.GetCurrentlyRunningPipeline(); + RestoreStoppingPipelineImpl(localPipeline, oldIsStopping); + } + + internal static bool SuspendStoppingPipelineImpl(LocalPipeline localPipeline) + { + if (localPipeline is not null) { - bool oldIsStopping = lpl.Stopper.IsStopping; - lpl.Stopper.IsStopping = false; + bool oldIsStopping = localPipeline.Stopper.IsStopping; + localPipeline.Stopper.IsStopping = false; return oldIsStopping; } return false; } - internal static void RestoreStoppingPipeline(ExecutionContext context, bool oldIsStopping) + internal static void RestoreStoppingPipelineImpl(LocalPipeline localPipeline, bool oldIsStopping) { - LocalPipeline lpl = (LocalPipeline)context.CurrentRunspace.GetCurrentlyRunningPipeline(); - if (lpl != null) + if (localPipeline is not null) { - lpl.Stopper.IsStopping = oldIsStopping; + localPipeline.Stopper.IsStopping = oldIsStopping; } } @@ -1726,10 +1731,7 @@ private static ActionPreference ProcessTraps(FunctionContext funcContext, ErrorRecord err = rte.ErrorRecord; // CurrentCommandProcessor is normally not null, but it is null // when executing some unit tests through reflection. - if (context.CurrentCommandProcessor != null) - { - context.CurrentCommandProcessor.ForgetScriptException(); - } + context.CurrentCommandProcessor?.ForgetScriptException(); try { @@ -1923,10 +1925,7 @@ internal static void SetErrorVariables(IScriptExtent extent, RuntimeException rt if (rte is not PipelineStoppedException) { - if (outputPipe != null) - { - outputPipe.AppendVariableList(VariableStreamKind.Error, errRec); - } + outputPipe?.AppendVariableList(VariableStreamKind.Error, errRec); context.AppendDollarError(errRec); } @@ -2855,6 +2854,11 @@ internal static object ForEach(IEnumerator enumerator, object expression, object ScriptBlock sb = expression as ScriptBlock; if (sb != null) { + if (sb.HasCleanBlock) + { + throw new PSNotSupportedException(ParserStrings.ForEachNotSupportCleanBlock); + } + Pipe outputPipe = new Pipe(result); if (sb.HasBeginBlock) { @@ -3497,10 +3501,7 @@ internal static void WriteEnumerableToPipe(IEnumerator enumerator, Pipe pipe, Ex if (dispose) { var disposable = enumerator as IDisposable; - if (disposable != null) - { - disposable.Dispose(); - } + disposable?.Dispose(); } } } @@ -3531,4 +3532,101 @@ internal static object[] GetSlice(IList list, int startIndex) return result; } } + + internal static class MemberInvocationLoggingOps + { + private static readonly Lazy DumpLogAMSIContent = new Lazy( + () => { + object result = Environment.GetEnvironmentVariable("__PSDumpAMSILogContent"); + if (result != null && LanguagePrimitives.TryConvertTo(result, out int value)) + { + return value == 1; + } + return false; + } + ); + + private static string ArgumentToString(object arg) + { + object baseObj = PSObject.Base(arg); + if (baseObj is null) + { + // The argument is null or AutomationNull.Value. + return "null"; + } + + // The comparisons below are ordered by the likelihood of arguments being of those types. + if (baseObj is string str) + { + return str; + } + + // Special case some types to call 'ToString' on the object. For the rest, we return its + // full type name to avoid calling a potentially expensive 'ToString' implementation. + Type baseType = baseObj.GetType(); + if (baseType.IsEnum || baseType.IsPrimitive + || baseType == typeof(Guid) + || baseType == typeof(Uri) + || baseType == typeof(Version) + || baseType == typeof(SemanticVersion) + || baseType == typeof(BigInteger) + || baseType == typeof(decimal)) + { + return baseObj.ToString(); + } + + return baseType.FullName; + } + + internal static void LogMemberInvocation(string targetName, string name, object[] args) + { + try + { + var contentName = "PowerShellMemberInvocation"; + var argsBuilder = new Text.StringBuilder(); + + for (int i = 0; i < args.Length; i++) + { + string value = ArgumentToString(args[i]); + + if (i > 0) + { + argsBuilder.Append(", "); + } + + argsBuilder.Append($"<{value}>"); + } + + string content = $"<{targetName}>.{name}({argsBuilder})"; + + if (DumpLogAMSIContent.Value) + { + Console.WriteLine("\n=== Amsi notification report content ==="); + Console.WriteLine(content); + } + + var success = AmsiUtils.ReportContent( + name: contentName, + content: content); + + if (DumpLogAMSIContent.Value) + { + Console.WriteLine($"=== Amsi notification report success: {success} ==="); + } + } + catch (PSSecurityException) + { + // ReportContent() will throw PSSecurityException if AMSI detects malware, which + // must be propagated. + throw; + } + catch (Exception ex) + { + if (DumpLogAMSIContent.Value) + { + Console.WriteLine($"!!! Amsi notification report exception: {ex} !!!"); + } + } + } + } } diff --git a/src/System.Management.Automation/engine/runtime/Operations/VariableOps.cs b/src/System.Management.Automation/engine/runtime/Operations/VariableOps.cs index 6799d1bd989..72126c726c9 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/VariableOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/VariableOps.cs @@ -46,6 +46,13 @@ internal static object SetVariableValue(VariablePath variablePath, object value, : GetAttributeCollection(attributeAsts); var = new PSVariable(variablePath.UnqualifiedPath, value, ScopedItemOptions.None, attributes); + if (attributes.Count > 0) + { + // When there are any attributes, it's possible the value was converted/transformed. + // Use 'GetValueRaw' here so the debugger check won't be triggered. + value = var.GetValueRaw(); + } + // Marking untrusted values for assignments in 'ConstrainedLanguage' mode is done in // SessionStateScope.SetVariable. sessionState.SetVariable(variablePath, var, false, origin); @@ -81,7 +88,7 @@ internal static object SetVariableValue(VariablePath variablePath, object value, null, Metadata.InvalidValueFailure, var.Name, - ((value != null) ? value.ToString() : "$null")); + (value != null) ? value.ToString() : "$null"); throw e; } diff --git a/src/System.Management.Automation/engine/runtime/ScriptBlockToPowerShell.cs b/src/System.Management.Automation/engine/runtime/ScriptBlockToPowerShell.cs index fc1dba4aed8..a3185149ecd 100644 --- a/src/System.Management.Automation/engine/runtime/ScriptBlockToPowerShell.cs +++ b/src/System.Management.Automation/engine/runtime/ScriptBlockToPowerShell.cs @@ -230,10 +230,7 @@ internal static PowerShell Convert(ScriptBlockAst body, { ExecutionContext.CheckStackDepth(); - if (args == null) - { - args = Array.Empty(); - } + args ??= Array.Empty(); // Perform validations on the ScriptBlock. GetSimplePipeline can allow for more than one // pipeline if the first parameter is true, but Invoke-Command doesn't yet support multiple @@ -324,13 +321,11 @@ internal static PowerShell Convert(ScriptBlockAst body, /// Scriptblock to search. /// True when input is trusted. /// Execution context. - /// List of foreach command names and aliases. /// Dictionary of using variable map. internal static Dictionary GetUsingValuesForEachParallel( ScriptBlock scriptBlock, bool isTrustedInput, - ExecutionContext context, - string[] foreachNames) + ExecutionContext context) { // Using variables for Foreach-Object -Parallel use are restricted to be within the // Foreach-Object -Parallel call scope. This will filter the using variable map to variables @@ -350,7 +345,7 @@ internal static Dictionary GetUsingValuesForEachParallel( for (int i = 0; i < usingAsts.Count; ++i) { usingAst = (UsingExpressionAst)usingAsts[i]; - if (IsInForeachParallelCallingScope(usingAst, foreachNames)) + if (IsInForeachParallelCallingScope(scriptBlock.Ast, usingAst)) { var value = Compiler.GetExpressionValue(usingAst.SubExpression, isTrustedInput, context); string usingAstKey = PsUtils.GetUsingExpressionKey(usingAst); @@ -382,17 +377,61 @@ internal static Dictionary GetUsingValuesForEachParallel( return usingValueMap; } + // List of Foreach-Object command names and aliases. + // TODO: Look into using SessionState.Internal.GetAliasTable() to find all user created aliases. + // But update Alias command logic to maintain reverse table that lists all aliases mapping + // to a single command definition, for performance. + private static readonly string[] forEachNames = new string[] + { + "ForEach-Object", + "foreach", + "%" + }; + + private static bool FindForEachInCommand(CommandAst commandAst) + { + // Command name is always the first element in the CommandAst. + // e.g., 'foreach -parallel {}' + var commandNameElement = (commandAst.CommandElements.Count > 0) ? commandAst.CommandElements[0] : null; + if (commandNameElement is StringConstantExpressionAst commandName) + { + bool found = false; + foreach (var foreachName in forEachNames) + { + if (commandName.Value.Equals(foreachName, StringComparison.OrdinalIgnoreCase)) + { + found = true; + break; + } + } + + if (found) + { + // Verify this is foreach-object with parallel parameter set. + var bindingResult = StaticParameterBinder.BindCommand(commandAst); + if (bindingResult.BoundParameters.ContainsKey("Parallel")) + { + return true; + } + } + } + + return false; + } + /// /// Walks the using Ast to verify it is used within a foreach-object -parallel command /// and parameter set scope, and not from within a nested foreach-object -parallel call. /// + /// Scriptblock Ast containing this using Ast /// Using Ast to check. - /// List of foreach-object command names. /// True if using expression is in current call scope. private static bool IsInForeachParallelCallingScope( - UsingExpressionAst usingAst, - string[] foreachNames) + Ast scriptblockAst, + UsingExpressionAst usingAst) { + Diagnostics.Assert(usingAst != null, "usingAst argument cannot be null."); + /* Example: $Test1 = "Hello" @@ -405,54 +444,23 @@ private static bool IsInForeachParallelCallingScope( } } */ - Diagnostics.Assert(usingAst != null, "usingAst argument cannot be null."); // Search up the parent Ast chain for 'Foreach-Object -Parallel' commands. Ast currentParent = usingAst.Parent; - int foreachNestedCount = 0; - while (currentParent != null) + while (currentParent != scriptblockAst) { // Look for Foreach-Object outer commands - if (currentParent is CommandAst commandAst) - { - foreach (var commandElement in commandAst.CommandElements) - { - if (commandElement is StringConstantExpressionAst commandName) - { - bool found = false; - foreach (var foreachName in foreachNames) - { - if (commandName.Value.Equals(foreachName, StringComparison.OrdinalIgnoreCase)) - { - found = true; - break; - } - } - - if (found) - { - // Verify this is foreach-object with parallel parameter set. - var bindingResult = StaticParameterBinder.BindCommand(commandAst); - if (bindingResult.BoundParameters.ContainsKey("Parallel")) - { - foreachNestedCount++; - break; - } - } - } - } - } - - if (foreachNestedCount > 1) + if (currentParent is CommandAst commandAst && + FindForEachInCommand(commandAst)) { - // This using expression Ast is outside the original calling scope. + // Using Ast is outside the invoking foreach scope. return false; } currentParent = currentParent.Parent; } - return foreachNestedCount == 1; + return true; } /// diff --git a/src/System.Management.Automation/engine/serialization.cs b/src/System.Management.Automation/engine/serialization.cs index ce21d8258be..76b7c38b7b3 100644 --- a/src/System.Management.Automation/engine/serialization.cs +++ b/src/System.Management.Automation/engine/serialization.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Specialized; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -3668,7 +3669,7 @@ private PSObject ReadPSObject() else if (IsKnownContainerTag(out ct)) { s_trace.WriteLine("Found container node {0}", ct); - baseObject = ReadKnownContainer(ct); + baseObject = ReadKnownContainer(ct, dso.InternalTypeNames); } else if (IsNextElement(SerializationStrings.PSObjectTag)) { @@ -3932,12 +3933,12 @@ private bool IsKnownContainerTag(out ContainerType ct) return ct != ContainerType.None; } - private object ReadKnownContainer(ContainerType ct) + private object ReadKnownContainer(ContainerType ct, ConsolidatedString InternalTypeNames) { switch (ct) { case ContainerType.Dictionary: - return ReadDictionary(ct); + return ReadDictionary(ct, InternalTypeNames); case ContainerType.Enumerable: case ContainerType.List: @@ -3986,19 +3987,81 @@ private object ReadListContainer(ContainerType ct) return list; } + /// + /// Utilitily class for ReadDictionary(), supporting ordered or non-ordered Dictionaty methods. + /// + private class PSDictionary + { + private IDictionary dict; + private readonly bool _isOrdered; + private int _keyClashFoundIteration = 0; + + public PSDictionary(bool isOrdered) { + _isOrdered = isOrdered; + + // By default use a non case-sensitive comparer + if (_isOrdered) { + dict = new OrderedDictionary(StringComparer.CurrentCultureIgnoreCase); + } else { + dict = new Hashtable(StringComparer.CurrentCultureIgnoreCase); + } + } + + public object DictionaryObject { get { return dict; } } + + public void Add(object key, object value) { + // On the first collision, copy the hash table to one that uses the `key` object's default comparer. + if (_keyClashFoundIteration == 0 && dict.Contains(key)) + { + _keyClashFoundIteration++; + IDictionary newDict = _isOrdered ? new OrderedDictionary(dict.Count) : new Hashtable(dict.Count); + + foreach (DictionaryEntry entry in dict) { + newDict.Add(entry.Key, entry.Value); + } + + dict = newDict; + } + + // win8: 389060. If there are still collisions even with case-sensitive default comparer, + // use an IEqualityComparer that does object ref equality. + if (_keyClashFoundIteration == 1 && dict.Contains(key)) + { + _keyClashFoundIteration++; + IEqualityComparer equalityComparer = new ReferenceEqualityComparer(); + IDictionary newDict = _isOrdered ? + new OrderedDictionary(dict.Count, equalityComparer) : + new Hashtable(dict.Count, equalityComparer); + + foreach (DictionaryEntry entry in dict) { + newDict.Add(entry.Key, entry.Value); + } + + dict = newDict; + } + + dict.Add(key, value); + } + } + /// /// Deserialize Dictionary. /// /// - private object ReadDictionary(ContainerType ct) + private object ReadDictionary(ContainerType ct, ConsolidatedString InternalTypeNames) { Dbg.Assert(ct == ContainerType.Dictionary, "Unrecognized ContainerType enum"); // We assume the hash table is a PowerShell hash table and hence uses // a case insensitive string comparer. If we discover a key collision, // we'll revert back to the default comparer. - Hashtable table = new Hashtable(StringComparer.CurrentCultureIgnoreCase); - int keyClashFoundIteration = 0; + + // Find whether original directory was ordered + bool isOrdered = InternalTypeNames.Count > 0 && + (Deserializer.MaskDeserializationPrefix(InternalTypeNames[0]) == typeof(OrderedDictionary).FullName); + + PSDictionary dictionary = new PSDictionary(isOrdered); + if (ReadStartElementAndHandleEmpty(SerializationStrings.DictionaryTag)) { while (_reader.NodeType == XmlNodeType.Element) @@ -4037,38 +4100,10 @@ private object ReadDictionary(ContainerType ct) object value = ReadOneObject(); - // On the first collision, copy the hash table to one that uses the default comparer. - if (table.ContainsKey(key) && (keyClashFoundIteration == 0)) - { - keyClashFoundIteration++; - Hashtable newHashTable = new Hashtable(); - foreach (DictionaryEntry entry in table) - { - newHashTable.Add(entry.Key, entry.Value); - } - - table = newHashTable; - } - - // win8: 389060. If there are still collisions even with case-sensitive default comparer, - // use an IEqualityComparer that does object ref equality. - if (table.ContainsKey(key) && (keyClashFoundIteration == 1)) - { - keyClashFoundIteration++; - IEqualityComparer equalityComparer = new ReferenceEqualityComparer(); - Hashtable newHashTable = new Hashtable(equalityComparer); - foreach (DictionaryEntry entry in table) - { - newHashTable.Add(entry.Key, entry.Value); - } - - table = newHashTable; - } - try { // Add entry to hashtable - table.Add(key, value); + dictionary.Add(key, value); } catch (ArgumentException e) { @@ -4081,7 +4116,7 @@ private object ReadDictionary(ContainerType ct) ReadEndElement(); } - return table; + return dictionary.DictionaryObject; } #endregion known containers @@ -7212,7 +7247,6 @@ internal static PSSenderInfo RehydratePSSenderInfo(PSObject pso) PSSenderInfo senderInfo = new PSSenderInfo(psPrincipal, GetPropertyValue(pso, "ConnectionString")); - senderInfo.ClientTimeZone = TimeZoneInfo.Local; senderInfo.ApplicationArguments = GetPropertyValue(pso, "ApplicationArguments"); return senderInfo; diff --git a/src/System.Management.Automation/help/BaseCommandHelpInfo.cs b/src/System.Management.Automation/help/BaseCommandHelpInfo.cs index 0c70b815928..5c50094abf3 100644 --- a/src/System.Management.Automation/help/BaseCommandHelpInfo.cs +++ b/src/System.Management.Automation/help/BaseCommandHelpInfo.cs @@ -356,15 +356,9 @@ internal override bool MatchPatternInContent(WildcardPattern pattern) string synopsis = Synopsis; string detailedDescription = DetailedDescription; - if (synopsis == null) - { - synopsis = string.Empty; - } + synopsis ??= string.Empty; - if (detailedDescription == null) - { - detailedDescription = string.Empty; - } + detailedDescription ??= string.Empty; return pattern.IsMatch(synopsis) || pattern.IsMatch(detailedDescription); } @@ -462,7 +456,7 @@ internal string DetailedDescription return string.Empty; } - // I think every cmdlet description should atleast have 400 characters... + // I think every cmdlet description should at least have 400 characters... // so starting with this assumption..I did an average of all the cmdlet // help content available at the time of writing this code and came up // with this number. diff --git a/src/System.Management.Automation/help/CabinetNativeApi.cs b/src/System.Management.Automation/help/CabinetNativeApi.cs index 76e8fe853a4..9abe5a894b4 100644 --- a/src/System.Management.Automation/help/CabinetNativeApi.cs +++ b/src/System.Management.Automation/help/CabinetNativeApi.cs @@ -70,10 +70,7 @@ protected override void Dispose(bool disposing) } // Free managed objects within 'if (disposing)' if needed - if (fdiContext != null) - { - fdiContext.Dispose(); - } + fdiContext?.Dispose(); // Free unmanaged objects here this.CleanUpDelegates(); diff --git a/src/System.Management.Automation/help/CommandHelpProvider.cs b/src/System.Management.Automation/help/CommandHelpProvider.cs index 2abfbeaee22..e32d1c86354 100644 --- a/src/System.Management.Automation/help/CommandHelpProvider.cs +++ b/src/System.Management.Automation/help/CommandHelpProvider.cs @@ -1275,7 +1275,7 @@ internal override IEnumerable ProcessForwardedHelp(HelpInfo helpInfo, } catch (CommandNotFoundException) { - // ignore errors for aliases pointing to non-existant commands + // ignore errors for aliases pointing to non-existent commands } } diff --git a/src/System.Management.Automation/help/HelpUtils.cs b/src/System.Management.Automation/help/HelpUtils.cs index d4f2abe255b..ab4a39f362a 100644 --- a/src/System.Management.Automation/help/HelpUtils.cs +++ b/src/System.Management.Automation/help/HelpUtils.cs @@ -41,7 +41,7 @@ internal static string GetModuleBaseForUserHelp(string moduleBase, string module // In case of other modules, the help is under moduleBase/ or // under moduleBase//. // The code below creates a similar layout for CurrentUser scope. - // If the the scope is AllUsers, then the help goes under moduleBase. + // If the scope is AllUsers, then the help goes under moduleBase. var userHelpPath = GetUserHomeHelpSearchPath(); string moduleBaseParent = Directory.GetParent(moduleBase).Name; diff --git a/src/System.Management.Automation/help/MamlCommandHelpInfo.cs b/src/System.Management.Automation/help/MamlCommandHelpInfo.cs index 774ef1fc1c0..81a9a4a743d 100644 --- a/src/System.Management.Automation/help/MamlCommandHelpInfo.cs +++ b/src/System.Management.Automation/help/MamlCommandHelpInfo.cs @@ -343,7 +343,7 @@ private string ExtractText(PSObject psObject) return string.Empty; } - // I think every cmdlet description should atleast have 400 characters... + // I think every cmdlet description should at least have 400 characters... // so starting with this assumption..I did an average of all the cmdlet // help content available at the time of writing this code and came up // with this number. diff --git a/src/System.Management.Automation/help/MamlNode.cs b/src/System.Management.Automation/help/MamlNode.cs index 8f8df05bb79..fa175391501 100644 --- a/src/System.Management.Automation/help/MamlNode.cs +++ b/src/System.Management.Automation/help/MamlNode.cs @@ -209,7 +209,7 @@ private PSObject GetPSObject(XmlNode xmlNode) { mshObject = new PSObject(GetInsidePSObject(xmlNode)); // Add typeNames to this MSHObject and create views so that - // the ouput is readable. This is done only for complex nodes. + // the output is readable. This is done only for complex nodes. mshObject.TypeNames.Clear(); if (xmlNode.Attributes["type"] != null) diff --git a/src/System.Management.Automation/help/PSClassHelpProvider.cs b/src/System.Management.Automation/help/PSClassHelpProvider.cs index e4c74c6398a..2d80b2cc863 100644 --- a/src/System.Management.Automation/help/PSClassHelpProvider.cs +++ b/src/System.Management.Automation/help/PSClassHelpProvider.cs @@ -279,7 +279,7 @@ private void LoadHelpFile(string helpFile, string helpFileIdentifier, string com } if (e != null) - s_tracer.WriteLine("Error occured in PSClassHelpProvider {0}", e.Message); + s_tracer.WriteLine("Error occurred in PSClassHelpProvider {0}", e.Message); if (reportErrors && (e != null)) { diff --git a/src/System.Management.Automation/help/ProviderHelpInfo.cs b/src/System.Management.Automation/help/ProviderHelpInfo.cs index 01ae3729420..80859a2ddf8 100644 --- a/src/System.Management.Automation/help/ProviderHelpInfo.cs +++ b/src/System.Management.Automation/help/ProviderHelpInfo.cs @@ -101,7 +101,7 @@ internal string DetailedDescription return string.Empty; } - // I think every provider description should atleast have 400 characters... + // I think every provider description should at least have 400 characters... // so starting with this assumption..I did an average of all the help content // available at the time of writing this code and came up with this number. Text.StringBuilder result = new Text.StringBuilder(400); @@ -167,15 +167,9 @@ internal override bool MatchPatternInContent(WildcardPattern pattern) string synopsis = Synopsis; string detailedDescription = DetailedDescription; - if (synopsis == null) - { - synopsis = string.Empty; - } + synopsis ??= string.Empty; - if (detailedDescription == null) - { - detailedDescription = string.Empty; - } + detailedDescription ??= string.Empty; return pattern.IsMatch(synopsis) || pattern.IsMatch(detailedDescription); } diff --git a/src/System.Management.Automation/help/SaveHelpCommand.cs b/src/System.Management.Automation/help/SaveHelpCommand.cs index 94803728c99..7f42a25a99a 100644 --- a/src/System.Management.Automation/help/SaveHelpCommand.cs +++ b/src/System.Management.Automation/help/SaveHelpCommand.cs @@ -260,10 +260,7 @@ internal override bool ProcessModuleWithCulture(UpdatableHelpModuleInfo module, } finally { - if (helpInfoDrive != null) - { - helpInfoDrive.Dispose(); - } + helpInfoDrive?.Dispose(); } } @@ -407,10 +404,7 @@ internal override bool ProcessModuleWithCulture(UpdatableHelpModuleInfo module, } finally { - if (helpContentDrive != null) - { - helpContentDrive.Dispose(); - } + helpContentDrive?.Dispose(); } } } diff --git a/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs b/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs index 49d1e67e5d9..af410e4bd6d 100644 --- a/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs +++ b/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs @@ -166,22 +166,22 @@ private void HandleProgressChanged(object sender, UpdatableHelpProgressEventArgs /// /// Static constructor /// - /// NOTE: FWLinks for core PowerShell modules are needed since they get loaded as snapins in a Remoting Endpoint. + /// NOTE: HelpInfoUri for core PowerShell modules are needed since they get loaded as snapins in a Remoting Endpoint. /// When we moved to modules in V3, we were not able to make this change as it was a risky change to make at that time. /// static UpdatableHelpCommandBase() { s_metadataCache = new Dictionary(StringComparer.OrdinalIgnoreCase); - // TODO: assign real TechNet addresses + // NOTE: The HelpInfoUri must be updated with each release. - s_metadataCache.Add("Microsoft.PowerShell.Diagnostics", "https://aka.ms/powershell71-help"); - s_metadataCache.Add("Microsoft.PowerShell.Core", "https://aka.ms/powershell71-help"); - s_metadataCache.Add("Microsoft.PowerShell.Utility", "https://aka.ms/powershell71-help"); - s_metadataCache.Add("Microsoft.PowerShell.Host", "https://aka.ms/powershell71-help"); - s_metadataCache.Add("Microsoft.PowerShell.Management", "https://aka.ms/powershell71-help"); - s_metadataCache.Add("Microsoft.PowerShell.Security", "https://aka.ms/powershell71-help"); - s_metadataCache.Add("Microsoft.WSMan.Management", "https://aka.ms/powershell71-help"); + s_metadataCache.Add("Microsoft.PowerShell.Diagnostics", "https://aka.ms/powershell73-help"); + s_metadataCache.Add("Microsoft.PowerShell.Core", "https://aka.ms/powershell73-help"); + s_metadataCache.Add("Microsoft.PowerShell.Utility", "https://aka.ms/powershell73-help"); + s_metadataCache.Add("Microsoft.PowerShell.Host", "https://aka.ms/powershell73-help"); + s_metadataCache.Add("Microsoft.PowerShell.Management", "https://aka.ms/powershell73-help"); + s_metadataCache.Add("Microsoft.PowerShell.Security", "https://aka.ms/powershell73-help"); + s_metadataCache.Add("Microsoft.WSMan.Management", "https://aka.ms/powershell73-help"); } /// diff --git a/src/System.Management.Automation/help/UpdatableHelpModuleInfo.cs b/src/System.Management.Automation/help/UpdatableHelpModuleInfo.cs index b46717920f4..ccf0b95f0f6 100644 --- a/src/System.Management.Automation/help/UpdatableHelpModuleInfo.cs +++ b/src/System.Management.Automation/help/UpdatableHelpModuleInfo.cs @@ -28,7 +28,6 @@ internal class UpdatableHelpModuleInfo internal UpdatableHelpModuleInfo(string name, Guid guid, string path, string uri) { Debug.Assert(!string.IsNullOrEmpty(name)); - Debug.Assert(guid != Guid.Empty); Debug.Assert(!string.IsNullOrEmpty(path)); Debug.Assert(!string.IsNullOrEmpty(uri)); diff --git a/src/System.Management.Automation/help/UpdatableHelpUri.cs b/src/System.Management.Automation/help/UpdatableHelpUri.cs index 8b854e82a5d..82bbb4016fb 100644 --- a/src/System.Management.Automation/help/UpdatableHelpUri.cs +++ b/src/System.Management.Automation/help/UpdatableHelpUri.cs @@ -21,7 +21,6 @@ internal class UpdatableHelpUri internal UpdatableHelpUri(string moduleName, Guid moduleGuid, CultureInfo culture, string resolvedUri) { Debug.Assert(!string.IsNullOrEmpty(moduleName)); - Debug.Assert(moduleGuid != Guid.Empty); Debug.Assert(!string.IsNullOrEmpty(resolvedUri)); ModuleName = moduleName; diff --git a/src/System.Management.Automation/help/UpdateHelpCommand.cs b/src/System.Management.Automation/help/UpdateHelpCommand.cs index 01b17cbad7e..371a7321492 100644 --- a/src/System.Management.Automation/help/UpdateHelpCommand.cs +++ b/src/System.Management.Automation/help/UpdateHelpCommand.cs @@ -322,10 +322,7 @@ internal override bool ProcessModuleWithCulture(UpdatableHelpModuleInfo module, } finally { - if (helpInfoDrive != null) - { - helpInfoDrive.Dispose(); - } + helpInfoDrive?.Dispose(); } } else diff --git a/src/System.Management.Automation/logging/MshLog.cs b/src/System.Management.Automation/logging/MshLog.cs index 89c8231311e..9d98ea8d0ab 100644 --- a/src/System.Management.Automation/logging/MshLog.cs +++ b/src/System.Management.Automation/logging/MshLog.cs @@ -134,11 +134,6 @@ private static Collection CreateLogProvider(string shellId) try { -#if !CORECLR // TODO:CORECLR EventLogLogProvider not handled yet - LogProvider eventLogLogProvider = new EventLogLogProvider(shellId); - providers.Add(eventLogLogProvider); -#endif - #if UNIX LogProvider sysLogProvider = new PSSysLogProvider(); providers.Add(sysLogProvider); diff --git a/src/System.Management.Automation/logging/eventlog/EventLogLogProvider.cs b/src/System.Management.Automation/logging/eventlog/EventLogLogProvider.cs deleted file mode 100644 index e6f301a04ac..00000000000 --- a/src/System.Management.Automation/logging/eventlog/EventLogLogProvider.cs +++ /dev/null @@ -1,684 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.Globalization; -using System.Resources; -using System.Text; -using System.Threading; - -namespace System.Management.Automation -{ - /// - /// EventLogLogProvider is a class to implement Msh Provider interface using EventLog technology. - /// - /// EventLogLogProvider will be the provider to use if Monad is running in early windows releases - /// from 2000 to 2003. - /// - /// EventLogLogProvider will be packaged in the same dll as Msh Log Engine since EventLog should - /// always be available. - /// - internal class EventLogLogProvider : LogProvider - { - /// - /// Constructor. - /// - /// - internal EventLogLogProvider(string shellId) - { - string source = SetupEventSource(shellId); - - _eventLog = new EventLog(); - _eventLog.Source = source; - - _resourceManager = new ResourceManager("System.Management.Automation.resources.Logging", System.Reflection.Assembly.GetExecutingAssembly()); - } - - internal string SetupEventSource(string shellId) - { - string source; - - // In case shellId == null, use the "Default" source. - if (string.IsNullOrEmpty(shellId)) - { - source = "Default"; - } - else - { - int index = shellId.LastIndexOf('.'); - - if (index < 0) - source = shellId; - else - source = shellId.Substring(index + 1); - - // There may be a situation where ShellId ends with a '.'. - // In that case, use the default source. - if (string.IsNullOrEmpty(source)) - source = "Default"; - } - - if (EventLog.SourceExists(source)) - { - return source; - } - - string message = string.Format(Thread.CurrentThread.CurrentCulture, "Event source '{0}' is not registered", source); - throw new InvalidOperationException(message); - } - - /// - /// This represent a handle to EventLog. - /// - private EventLog _eventLog; - private ResourceManager _resourceManager; - - #region Log Provider Api - - private const int EngineHealthCategoryId = 1; - private const int CommandHealthCategoryId = 2; - private const int ProviderHealthCategoryId = 3; - private const int EngineLifecycleCategoryId = 4; - private const int CommandLifecycleCategoryId = 5; - private const int ProviderLifecycleCategoryId = 6; - private const int SettingsCategoryId = 7; - private const int PipelineExecutionDetailCategoryId = 8; - - /// - /// Log engine health event. - /// - /// - /// - /// - /// - internal override void LogEngineHealthEvent(LogContext logContext, int eventId, Exception exception, Dictionary additionalInfo) - { - Hashtable mapArgs = new Hashtable(); - - IContainsErrorRecord icer = exception as IContainsErrorRecord; - if (icer != null && icer.ErrorRecord != null) - { - mapArgs["ExceptionClass"] = exception.GetType().Name; - mapArgs["ErrorCategory"] = icer.ErrorRecord.CategoryInfo.Category; - mapArgs["ErrorId"] = icer.ErrorRecord.FullyQualifiedErrorId; - - if (icer.ErrorRecord.ErrorDetails != null) - { - mapArgs["ErrorMessage"] = icer.ErrorRecord.ErrorDetails.Message; - } - else - { - mapArgs["ErrorMessage"] = exception.Message; - } - } - else - { - mapArgs["ExceptionClass"] = exception.GetType().Name; - mapArgs["ErrorCategory"] = string.Empty; - mapArgs["ErrorId"] = string.Empty; - mapArgs["ErrorMessage"] = exception.Message; - } - - FillEventArgs(mapArgs, logContext); - - FillEventArgs(mapArgs, additionalInfo); - - EventInstance entry = new EventInstance(eventId, EngineHealthCategoryId); - - entry.EntryType = GetEventLogEntryType(logContext); - - string detail = GetEventDetail("EngineHealthContext", mapArgs); - - LogEvent(entry, mapArgs["ErrorMessage"], detail); - } - - private static EventLogEntryType GetEventLogEntryType(LogContext logContext) - { - switch (logContext.Severity) - { - case "Critical": - case "Error": - return EventLogEntryType.Error; - case "Warning": - return EventLogEntryType.Warning; - default: - return EventLogEntryType.Information; - } - } - - /// - /// Log engine lifecycle event. - /// - /// - /// - /// - internal override void LogEngineLifecycleEvent(LogContext logContext, EngineState newState, EngineState previousState) - { - int eventId = GetEngineLifecycleEventId(newState); - - if (eventId == _invalidEventId) - return; - - Hashtable mapArgs = new Hashtable(); - - mapArgs["NewEngineState"] = newState.ToString(); - mapArgs["PreviousEngineState"] = previousState.ToString(); - - FillEventArgs(mapArgs, logContext); - - EventInstance entry = new EventInstance(eventId, EngineLifecycleCategoryId); - - entry.EntryType = EventLogEntryType.Information; - - string detail = GetEventDetail("EngineLifecycleContext", mapArgs); - - LogEvent(entry, newState, previousState, detail); - } - - private const int _baseEngineLifecycleEventId = 400; - private const int _invalidEventId = -1; - - /// - /// Get engine lifecycle event id based on engine state. - /// - /// - /// - private static int GetEngineLifecycleEventId(EngineState engineState) - { - switch (engineState) - { - case EngineState.None: - return _invalidEventId; - case EngineState.Available: - return _baseEngineLifecycleEventId; - case EngineState.Degraded: - return _baseEngineLifecycleEventId + 1; - case EngineState.OutOfService: - return _baseEngineLifecycleEventId + 2; - case EngineState.Stopped: - return _baseEngineLifecycleEventId + 3; - } - - return _invalidEventId; - } - - private const int _commandHealthEventId = 200; - - /// - /// Provider interface function for logging command health event. - /// - /// - /// - internal override void LogCommandHealthEvent(LogContext logContext, Exception exception) - { - int eventId = _commandHealthEventId; - - Hashtable mapArgs = new Hashtable(); - - IContainsErrorRecord icer = exception as IContainsErrorRecord; - if (icer != null && icer.ErrorRecord != null) - { - mapArgs["ExceptionClass"] = exception.GetType().Name; - mapArgs["ErrorCategory"] = icer.ErrorRecord.CategoryInfo.Category; - mapArgs["ErrorId"] = icer.ErrorRecord.FullyQualifiedErrorId; - - if (icer.ErrorRecord.ErrorDetails != null) - { - mapArgs["ErrorMessage"] = icer.ErrorRecord.ErrorDetails.Message; - } - else - { - mapArgs["ErrorMessage"] = exception.Message; - } - } - else - { - mapArgs["ExceptionClass"] = exception.GetType().Name; - mapArgs["ErrorCategory"] = string.Empty; - mapArgs["ErrorId"] = string.Empty; - mapArgs["ErrorMessage"] = exception.Message; - } - - FillEventArgs(mapArgs, logContext); - - EventInstance entry = new EventInstance(eventId, CommandHealthCategoryId); - - entry.EntryType = GetEventLogEntryType(logContext); - - string detail = GetEventDetail("CommandHealthContext", mapArgs); - - LogEvent(entry, mapArgs["ErrorMessage"], detail); - } - - /// - /// Log command life cycle event. - /// - /// - /// - internal override void LogCommandLifecycleEvent(Func getLogContext, CommandState newState) - { - LogContext logContext = getLogContext(); - - int eventId = GetCommandLifecycleEventId(newState); - - if (eventId == _invalidEventId) - return; - - Hashtable mapArgs = new Hashtable(); - - mapArgs["NewCommandState"] = newState.ToString(); - - FillEventArgs(mapArgs, logContext); - - EventInstance entry = new EventInstance(eventId, CommandLifecycleCategoryId); - - entry.EntryType = EventLogEntryType.Information; - - string detail = GetEventDetail("CommandLifecycleContext", mapArgs); - - LogEvent(entry, logContext.CommandName, newState, detail); - } - - private const int _baseCommandLifecycleEventId = 500; - - /// - /// Get command lifecycle event id based on command state. - /// - /// - /// - private static int GetCommandLifecycleEventId(CommandState commandState) - { - switch (commandState) - { - case CommandState.Started: - return _baseCommandLifecycleEventId; - case CommandState.Stopped: - return _baseCommandLifecycleEventId + 1; - case CommandState.Terminated: - return _baseCommandLifecycleEventId + 2; - } - - return _invalidEventId; - } - - private const int _pipelineExecutionDetailEventId = 800; - - /// - /// Log pipeline execution detail event. - /// - /// This may end of logging more than one event if the detail string is too long to be fit in 64K. - /// - /// - /// - internal override void LogPipelineExecutionDetailEvent(LogContext logContext, List pipelineExecutionDetail) - { - List details = GroupMessages(pipelineExecutionDetail); - - for (int i = 0; i < details.Count; i++) - { - LogPipelineExecutionDetailEvent(logContext, details[i], i + 1, details.Count); - } - } - - private const int MaxLength = 16000; - - private List GroupMessages(List messages) - { - List result = new List(); - - if (messages == null || messages.Count == 0) - return result; - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < messages.Count; i++) - { - if (sb.Length + messages[i].Length < MaxLength) - { - sb.AppendLine(messages[i]); - continue; - } - - result.Add(sb.ToString()); - sb = new StringBuilder(); - sb.AppendLine(messages[i]); - } - - result.Add(sb.ToString()); - - return result; - } - - /// - /// Log one pipeline execution detail event. Detail message is already chopped up so that it will - /// fit in 64K. - /// - /// - /// - /// - /// - private void LogPipelineExecutionDetailEvent(LogContext logContext, string pipelineExecutionDetail, int detailSequence, int detailTotal) - { - int eventId = _pipelineExecutionDetailEventId; - - Hashtable mapArgs = new Hashtable(); - - mapArgs["PipelineExecutionDetail"] = pipelineExecutionDetail; - mapArgs["DetailSequence"] = detailSequence; - mapArgs["DetailTotal"] = detailTotal; - - FillEventArgs(mapArgs, logContext); - - EventInstance entry = new EventInstance(eventId, PipelineExecutionDetailCategoryId); - - entry.EntryType = EventLogEntryType.Information; - - string pipelineInfo = GetEventDetail("PipelineExecutionDetailContext", mapArgs); - - LogEvent(entry, logContext.CommandLine, pipelineInfo, pipelineExecutionDetail); - } - - private const int _providerHealthEventId = 300; - /// - /// Provider interface function for logging provider health event. - /// - /// - /// - /// - internal override void LogProviderHealthEvent(LogContext logContext, string providerName, Exception exception) - { - int eventId = _providerHealthEventId; - - Hashtable mapArgs = new Hashtable(); - - mapArgs["ProviderName"] = providerName; - - IContainsErrorRecord icer = exception as IContainsErrorRecord; - if (icer != null && icer.ErrorRecord != null) - { - mapArgs["ExceptionClass"] = exception.GetType().Name; - mapArgs["ErrorCategory"] = icer.ErrorRecord.CategoryInfo.Category; - mapArgs["ErrorId"] = icer.ErrorRecord.FullyQualifiedErrorId; - - if (icer.ErrorRecord.ErrorDetails != null - && !string.IsNullOrEmpty(icer.ErrorRecord.ErrorDetails.Message)) - { - mapArgs["ErrorMessage"] = icer.ErrorRecord.ErrorDetails.Message; - } - else - { - mapArgs["ErrorMessage"] = exception.Message; - } - } - else - { - mapArgs["ExceptionClass"] = exception.GetType().Name; - mapArgs["ErrorCategory"] = string.Empty; - mapArgs["ErrorId"] = string.Empty; - mapArgs["ErrorMessage"] = exception.Message; - } - - FillEventArgs(mapArgs, logContext); - - EventInstance entry = new EventInstance(eventId, ProviderHealthCategoryId); - - entry.EntryType = GetEventLogEntryType(logContext); - - string detail = GetEventDetail("ProviderHealthContext", mapArgs); - - LogEvent(entry, mapArgs["ErrorMessage"], detail); - } - - /// - /// Log provider lifecycle event. - /// - /// - /// - /// - internal override void LogProviderLifecycleEvent(LogContext logContext, string providerName, ProviderState newState) - { - int eventId = GetProviderLifecycleEventId(newState); - - if (eventId == _invalidEventId) - return; - - Hashtable mapArgs = new Hashtable(); - - mapArgs["ProviderName"] = providerName; - mapArgs["NewProviderState"] = newState.ToString(); - - FillEventArgs(mapArgs, logContext); - - EventInstance entry = new EventInstance(eventId, ProviderLifecycleCategoryId); - - entry.EntryType = EventLogEntryType.Information; - - string detail = GetEventDetail("ProviderLifecycleContext", mapArgs); - - LogEvent(entry, providerName, newState, detail); - } - - private const int _baseProviderLifecycleEventId = 600; - - /// - /// Get provider lifecycle event id based on provider state. - /// - /// - /// - private static int GetProviderLifecycleEventId(ProviderState providerState) - { - switch (providerState) - { - case ProviderState.Started: - return _baseProviderLifecycleEventId; - case ProviderState.Stopped: - return _baseProviderLifecycleEventId + 1; - } - - return _invalidEventId; - } - - private const int _settingsEventId = 700; - - /// - /// Log settings event. - /// - /// - /// - /// - /// - internal override void LogSettingsEvent(LogContext logContext, string variableName, string value, string previousValue) - { - int eventId = _settingsEventId; - - Hashtable mapArgs = new Hashtable(); - - mapArgs["VariableName"] = variableName; - mapArgs["NewValue"] = value; - mapArgs["PreviousValue"] = previousValue; - - FillEventArgs(mapArgs, logContext); - - EventInstance entry = new EventInstance(eventId, SettingsCategoryId); - - entry.EntryType = EventLogEntryType.Information; - - string detail = GetEventDetail("SettingsContext", mapArgs); - - LogEvent(entry, variableName, value, previousValue, detail); - } - - #endregion Log Provider Api - - #region EventLog helper functions - - /// - /// This is the helper function for logging an event with localizable message - /// to event log. It will trace all exception thrown by eventlog. - /// - /// - /// - private void LogEvent(EventInstance entry, params object[] args) - { - try - { - _eventLog.WriteEvent(entry, args); - } - catch (ArgumentException) - { - return; - } - catch (InvalidOperationException) - { - return; - } - catch (Win32Exception) - { - return; - } - } - - #endregion - - #region Event Arguments - - /// - /// Fill event arguments with logContext info. - /// - /// In EventLog Api, arguments are passed in as an array of objects. - /// - /// An ArrayList to contain the event arguments. - /// The log context containing the info to fill in. - private static void FillEventArgs(Hashtable mapArgs, LogContext logContext) - { - mapArgs["Severity"] = logContext.Severity; - mapArgs["SequenceNumber"] = logContext.SequenceNumber; - mapArgs["HostName"] = logContext.HostName; - mapArgs["HostVersion"] = logContext.HostVersion; - mapArgs["HostId"] = logContext.HostId; - mapArgs["HostApplication"] = logContext.HostApplication; - mapArgs["EngineVersion"] = logContext.EngineVersion; - mapArgs["RunspaceId"] = logContext.RunspaceId; - mapArgs["PipelineId"] = logContext.PipelineId; - mapArgs["CommandName"] = logContext.CommandName; - mapArgs["CommandType"] = logContext.CommandType; - mapArgs["ScriptName"] = logContext.ScriptName; - mapArgs["CommandPath"] = logContext.CommandPath; - mapArgs["CommandLine"] = logContext.CommandLine; - mapArgs["User"] = logContext.User; - mapArgs["Time"] = logContext.Time; - } - - /// - /// Fill event arguments with additionalInfo stored in a string dictionary. - /// - /// An arraylist to contain the event arguments. - /// A string dictionary to fill in. - private static void FillEventArgs(Hashtable mapArgs, Dictionary additionalInfo) - { - if (additionalInfo == null) - { - for (int i = 0; i < 3; i++) - { - string id = ((int)(i + 1)).ToString("d1", CultureInfo.CurrentCulture); - - mapArgs["AdditionalInfo_Name" + id] = string.Empty; - mapArgs["AdditionalInfo_Value" + id] = string.Empty; - } - - return; - } - - string[] keys = new string[additionalInfo.Count]; - string[] values = new string[additionalInfo.Count]; - - additionalInfo.Keys.CopyTo(keys, 0); - additionalInfo.Values.CopyTo(values, 0); - for (int i = 0; i < 3; i++) - { - string id = ((int)(i + 1)).ToString("d1", CultureInfo.CurrentCulture); - - if (i < keys.Length) - { - mapArgs["AdditionalInfo_Name" + id] = keys[i]; - mapArgs["AdditionalInfo_Value" + id] = values[i]; - } - else - { - mapArgs["AdditionalInfo_Name" + id] = string.Empty; - mapArgs["AdditionalInfo_Value" + id] = string.Empty; - } - } - - return; - } - - #endregion Event Arguments - - #region Event Message - - private string GetEventDetail(string contextId, Hashtable mapArgs) - { - return GetMessage(contextId, mapArgs); - } - - private string GetMessage(string messageId, Hashtable mapArgs) - { - if (_resourceManager == null) - return string.Empty; - - string messageTemplate = _resourceManager.GetString(messageId); - - if (string.IsNullOrEmpty(messageTemplate)) - return string.Empty; - - return FillMessageTemplate(messageTemplate, mapArgs); - } - - private static string FillMessageTemplate(string messageTemplate, Hashtable mapArgs) - { - StringBuilder message = new StringBuilder(); - - int cursor = 0; - - while (true) - { - int startIndex = messageTemplate.IndexOf('[', cursor); - - if (startIndex < 0) - { - message.Append(messageTemplate.Substring(cursor)); - return message.ToString(); - } - - int endIndex = messageTemplate.IndexOf(']', startIndex + 1); - - if (endIndex < 0) - { - message.Append(messageTemplate.Substring(cursor)); - return message.ToString(); - } - - message.Append(messageTemplate.Substring(cursor, startIndex - cursor)); - cursor = startIndex; - - string placeHolder = messageTemplate.Substring(startIndex + 1, endIndex - startIndex - 1); - - if (mapArgs.Contains(placeHolder)) - { - message.Append(mapArgs[placeHolder]); - cursor = endIndex + 1; - } - else - { - message.Append("["); - cursor++; - } - } - } - - #endregion Event Message - } -} diff --git a/src/System.Management.Automation/namespaces/AliasProvider.cs b/src/System.Management.Automation/namespaces/AliasProvider.cs index 8782a0d9bb5..c51d8e35128 100644 --- a/src/System.Management.Automation/namespaces/AliasProvider.cs +++ b/src/System.Management.Automation/namespaces/AliasProvider.cs @@ -194,11 +194,7 @@ internal override void SetSessionStateItem(string name, object value, bool write if (dynamicParametersSpecified) { item = (AliasInfo)GetSessionStateItem(name); - - if (item != null) - { - item.SetOptions(dynamicParameters.Options, Force); - } + item?.SetOptions(dynamicParameters.Options, Force); } else { diff --git a/src/System.Management.Automation/namespaces/CoreCommandContext.cs b/src/System.Management.Automation/namespaces/CoreCommandContext.cs index 5446b6b71cb..5c62ed6fa98 100644 --- a/src/System.Management.Automation/namespaces/CoreCommandContext.cs +++ b/src/System.Management.Automation/namespaces/CoreCommandContext.cs @@ -390,13 +390,8 @@ private void CopyFilters(CmdletProviderContext context) Filter = context.Filter; } - internal void RemoveStopReferral() - { - if (_copiedContext != null) - { - _copiedContext.StopReferrals.Remove(this); - } - } + internal void RemoveStopReferral() => _copiedContext?.StopReferrals.Remove(this); + #endregion Internal properties #region Public properties @@ -774,13 +769,7 @@ internal bool ShouldContinue( /// /// The string that needs to be written. /// - internal void WriteVerbose(string text) - { - if (_command != null) - { - _command.WriteVerbose(text); - } - } + internal void WriteVerbose(string text) => _command?.WriteVerbose(text); /// /// Writes the object to the Warning pipe. @@ -788,21 +777,9 @@ internal void WriteVerbose(string text) /// /// The string that needs to be written. /// - internal void WriteWarning(string text) - { - if (_command != null) - { - _command.WriteWarning(text); - } - } + internal void WriteWarning(string text) => _command?.WriteWarning(text); - internal void WriteProgress(ProgressRecord record) - { - if (_command != null) - { - _command.WriteProgress(record); - } - } + internal void WriteProgress(ProgressRecord record) => _command?.WriteProgress(record); /// /// Writes a debug string. @@ -810,29 +787,11 @@ internal void WriteProgress(ProgressRecord record) /// /// The String that needs to be written. /// - internal void WriteDebug(string text) - { - if (_command != null) - { - _command.WriteDebug(text); - } - } + internal void WriteDebug(string text) => _command?.WriteDebug(text); - internal void WriteInformation(InformationRecord record) - { - if (_command != null) - { - _command.WriteInformation(record); - } - } + internal void WriteInformation(InformationRecord record) => _command?.WriteInformation(record); - internal void WriteInformation(object messageData, string[] tags) - { - if (_command != null) - { - _command.WriteInformation(messageData, tags); - } - } + internal void WriteInformation(object messageData, string[] tags) => _command?.WriteInformation(messageData, tags); #endregion User feedback mechanisms @@ -1154,14 +1113,10 @@ internal void StopProcessing() { Stopping = true; - if (_providerInstance != null) - { - // We don't need to catch any of the exceptions here because - // we are terminating the pipeline and any exception will - // be caught by the engine. - - _providerInstance.StopProcessing(); - } + // We don't need to catch any of the exceptions here because + // we are terminating the pipeline and any exception will + // be caught by the engine. + _providerInstance?.StopProcessing(); // Call the stop referrals if any diff --git a/src/System.Management.Automation/namespaces/FileSystemContentStream.cs b/src/System.Management.Automation/namespaces/FileSystemContentStream.cs index 1a9935cb1ad..352298107d5 100644 --- a/src/System.Management.Automation/namespaces/FileSystemContentStream.cs +++ b/src/System.Management.Automation/namespaces/FileSystemContentStream.cs @@ -413,7 +413,7 @@ public IList Read(long readCount) (e is UnauthorizedAccessException) || (e is ArgumentNullException)) { - // Exception contains specific message about the error occured and so no need for errordetails. + // Exception contains specific message about the error occurred and so no need for errordetails. _provider.WriteError(new ErrorRecord(e, "GetContentReaderIOError", ErrorCategory.ReadError, _path)); return null; } @@ -987,9 +987,8 @@ private void WaitForChanges(string filePath, FileMode fileMode, FileAccess fileA // Seek to the place we last left off. _stream.Seek(_fileOffset, SeekOrigin.Begin); - if (_reader != null) { _reader.DiscardBufferedData(); } - - if (_backReader != null) { _backReader.DiscardBufferedData(); } + _reader?.DiscardBufferedData(); + _backReader?.DiscardBufferedData(); } /// @@ -1003,15 +1002,13 @@ private void WaitForChanges(string filePath, FileMode fileMode, FileAccess fileA /// public void Seek(long offset, SeekOrigin origin) { - if (_writer != null) { _writer.Flush(); } + _writer?.Flush(); _stream.Seek(offset, origin); - if (_writer != null) { _writer.Flush(); } - - if (_reader != null) { _reader.DiscardBufferedData(); } - - if (_backReader != null) { _backReader.DiscardBufferedData(); } + _writer?.Flush(); + _reader?.DiscardBufferedData(); + _backReader?.DiscardBufferedData(); } /// @@ -1135,14 +1132,10 @@ internal void Dispose(bool isDisposing) { if (isDisposing) { - if (_stream != null) - _stream.Dispose(); - if (_reader != null) - _reader.Dispose(); - if (_backReader != null) - _backReader.Dispose(); - if (_writer != null) - _writer.Dispose(); + _stream?.Dispose(); + _reader?.Dispose(); + _backReader?.Dispose(); + _writer?.Dispose(); } } } diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 34c399f7bed..07625fc2ab0 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -37,6 +37,7 @@ namespace Microsoft.PowerShell.Commands [OutputType(typeof(FileSecurity), ProviderCmdlet = ProviderCmdlet.SetAcl)] [OutputType(typeof(string), typeof(PathInfo), ProviderCmdlet = ProviderCmdlet.ResolvePath)] [OutputType(typeof(PathInfo), ProviderCmdlet = ProviderCmdlet.PushLocation)] + [OutputType(typeof(PathInfo), ProviderCmdlet = ProviderCmdlet.PopLocation)] [OutputType(typeof(byte), typeof(string), ProviderCmdlet = ProviderCmdlet.GetContent)] [OutputType(typeof(FileInfo), ProviderCmdlet = ProviderCmdlet.GetItem)] [OutputType(typeof(FileInfo), typeof(DirectoryInfo), ProviderCmdlet = ProviderCmdlet.GetChildItem)] @@ -131,20 +132,21 @@ private static string GetCorrectCasedPath(string path) itemsToSkip = 4; } - foreach (string item in path.Split(StringLiterals.DefaultPathSeparator)) + var items = path.Split(StringLiterals.DefaultPathSeparator); + for (int i = 0; i < items.Length; i++) { if (itemsToSkip-- > 0) { // This handles the UNC server and share and 8.3 short path syntax - exactPath += item + StringLiterals.DefaultPathSeparator; + exactPath += items[i] + StringLiterals.DefaultPathSeparator; continue; } else if (string.IsNullOrEmpty(exactPath)) { // This handles the drive letter or / root path start - exactPath = item + StringLiterals.DefaultPathSeparator; + exactPath = items[i] + StringLiterals.DefaultPathSeparator; } - else if (string.IsNullOrEmpty(item)) + else if (string.IsNullOrEmpty(items[i]) && i == items.Length - 1) { // This handles the trailing slash case if (!exactPath.EndsWith(StringLiterals.DefaultPathSeparator)) @@ -154,17 +156,17 @@ private static string GetCorrectCasedPath(string path) break; } - else if (item.Contains('~')) + else if (items[i].Contains('~')) { // This handles short path names - exactPath += StringLiterals.DefaultPathSeparator + item; + exactPath += StringLiterals.DefaultPathSeparator + items[i]; } else { // Use GetFileSystemEntries to get the correct casing of this element try { - var entries = Directory.GetFileSystemEntries(exactPath, item); + var entries = Directory.GetFileSystemEntries(exactPath, items[i]); if (entries.Length > 0) { exactPath = entries[0]; @@ -469,9 +471,7 @@ protected override ProviderInfo Start(ProviderInfo providerInfo) #if !UNIX // The placeholder mode management APIs Rtl(Set|Query)(Process|Thread)PlaceholderCompatibilityMode // are only supported starting with Windows 10 version 1803 (build 17134) - Version minBuildForPlaceHolderAPIs = new Version(10, 0, 17134, 0); - - if (Environment.OSVersion.Version >= minBuildForPlaceHolderAPIs) + if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134, 0)) { // let's be safe, don't change the PlaceHolderCompatibilityMode if the current one is not what we expect if (NativeMethods.RtlQueryProcessPlaceholderCompatibilityMode() == NativeMethods.PHCM_DISGUISE_PLACEHOLDER) @@ -1374,13 +1374,18 @@ private FileSystemInfo GetFileSystemItem(string path, ref bool isContainer, bool path = NormalizePath(path); FileInfo result = new FileInfo(path); - // FileInfo.Exists is always false for a directory path, so we check the attribute for existence. var attributes = result.Attributes; - if ((int)attributes == -1) { /* Path doesn't exist. */ return null; } - bool hidden = attributes.HasFlag(FileAttributes.Hidden); isContainer = attributes.HasFlag(FileAttributes.Directory); + // FileInfo allows for a file path to end in a trailing slash, but the resulting object + // is incomplete. A trailing slash should indicate a directory. So if the path ends in a + // trailing slash and is not a directory, return null + if (!isContainer && path.EndsWith(Path.DirectorySeparatorChar)) + { + return null; + } + FlagsExpression evaluator = null; FlagsExpression switchEvaluator = null; GetChildDynamicParameters fspDynamicParam = DynamicParameters as GetChildDynamicParameters; @@ -2063,43 +2068,32 @@ string ToModeString(FileSystemInfo fileSystemInfo) /// Name if a file or directory, Name -> Target if symlink. public static string NameString(PSObject instance) { - if (ExperimentalFeature.IsEnabled("PSAnsiRenderingFileInfo")) + if (instance?.BaseObject is FileSystemInfo fileInfo) { - if (instance?.BaseObject is FileSystemInfo fileInfo) + if (InternalSymbolicLinkLinkCodeMethods.IsReparsePointLikeSymlink(fileInfo)) { - if (InternalSymbolicLinkLinkCodeMethods.IsReparsePointLikeSymlink(fileInfo)) - { - return $"{PSStyle.Instance.FileInfo.SymbolicLink}{fileInfo.Name}{PSStyle.Instance.Reset} -> {InternalSymbolicLinkLinkCodeMethods.GetTarget(instance)}"; - } - else if (fileInfo.Attributes.HasFlag(FileAttributes.Directory)) - { - return $"{PSStyle.Instance.FileInfo.Directory}{fileInfo.Name}{PSStyle.Instance.Reset}"; - } - else if (PSStyle.Instance.FileInfo.Extension.ContainsKey(fileInfo.Extension)) - { - return $"{PSStyle.Instance.FileInfo.Extension[fileInfo.Extension]}{fileInfo.Name}{PSStyle.Instance.Reset}"; - } - else if ((Platform.IsWindows && CommandDiscovery.PathExtensions.Contains(fileInfo.Extension.ToLower())) || - (!Platform.IsWindows && Platform.NonWindowsIsExecutable(fileInfo.FullName))) - { - return $"{PSStyle.Instance.FileInfo.Executable}{fileInfo.Name}{PSStyle.Instance.Reset}"; - } - else - { - return fileInfo.Name; - } + return $"{PSStyle.Instance.FileInfo.SymbolicLink}{fileInfo.Name}{PSStyle.Instance.Reset} -> {fileInfo.LinkTarget}"; + } + else if (fileInfo.Attributes.HasFlag(FileAttributes.Directory)) + { + return $"{PSStyle.Instance.FileInfo.Directory}{fileInfo.Name}{PSStyle.Instance.Reset}"; + } + else if (PSStyle.Instance.FileInfo.Extension.ContainsKey(fileInfo.Extension)) + { + return $"{PSStyle.Instance.FileInfo.Extension[fileInfo.Extension]}{fileInfo.Name}{PSStyle.Instance.Reset}"; + } + else if ((Platform.IsWindows && CommandDiscovery.PathExtensions.Contains(fileInfo.Extension.ToLower())) || + (!Platform.IsWindows && Platform.NonWindowsIsExecutable(fileInfo.FullName))) + { + return $"{PSStyle.Instance.FileInfo.Executable}{fileInfo.Name}{PSStyle.Instance.Reset}"; + } + else + { + return fileInfo.Name; } - - return string.Empty; - } - else - { - return instance?.BaseObject is FileSystemInfo fileInfo - ? InternalSymbolicLinkLinkCodeMethods.IsReparsePointLikeSymlink(fileInfo) - ? $"{fileInfo.Name} -> {InternalSymbolicLinkLinkCodeMethods.GetTarget(instance)}" - : fileInfo.Name - : string.Empty; } + + return string.Empty; } /// @@ -2724,8 +2718,7 @@ private static bool WinCreateSymbolicLink(string path, string strTargetPath, boo // The new AllowUnprivilegedCreate is only available on Win10 build 14972 or newer var flags = isDirectory ? NativeMethods.SymbolicLinkFlags.Directory : NativeMethods.SymbolicLinkFlags.File; - Version minBuildOfDeveloperMode = new Version(10, 0, 14972, 0); - if (Environment.OSVersion.Version >= minBuildOfDeveloperMode) + if (OperatingSystem.IsWindowsVersionAtLeast(10, 0, 14972, 0)) { flags |= NativeMethods.SymbolicLinkFlags.AllowUnprivilegedCreate; } @@ -4516,10 +4509,7 @@ private bool PerformCopyFileFromRemoteSession(string sourceFileFullName, FileInf } finally { - if (wStream != null) - { - wStream.Dispose(); - } + wStream?.Dispose(); // If copying the file from the remote session failed, then remove it. if (errorWhileCopyRemoteFile && File.Exists(destinationFile.FullName)) @@ -4799,10 +4789,7 @@ private bool CopyFileStreamToRemoteSession(FileInfo file, string destinationPath } finally { - if (fStream != null) - { - fStream.Dispose(); - } + fStream?.Dispose(); } return success; @@ -5113,10 +5100,7 @@ protected override string NormalizeRelativePath( throw PSTraceSource.NewArgumentException(nameof(path)); } - if (basePath == null) - { - basePath = string.Empty; - } + basePath ??= string.Empty; s_tracer.WriteLine("basePath = {0}", basePath); @@ -5305,10 +5289,7 @@ private string NormalizeRelativePathHelper(string path, string basePath) return string.Empty; } - if (basePath == null) - { - basePath = string.Empty; - } + basePath ??= string.Empty; s_tracer.WriteLine("basePath = {0}", basePath); @@ -5851,6 +5832,15 @@ protected override void MoveItem( destination = MakePath(destination, dir.Name); } + // Don't allow moving a directory into itself + if (destination.StartsWith(Path.TrimEndingDirectorySeparator(path) + Path.DirectorySeparatorChar)) + { + string error = StringUtil.Format(FileSystemProviderStrings.TargetCannotBeSubdirectoryOfSource, destination); + var e = new IOException(error); + WriteError(new ErrorRecord(e, "MoveItemArgumentError", ErrorCategory.InvalidArgument, destination)); + return; + } + // Get the confirmation text string action = FileSystemProviderStrings.MoveItemActionDirectory; @@ -6236,10 +6226,7 @@ public void GetProperty(string path, Collection providerSpecificPickList if (member != null) { value = member.Value; - if (result == null) - { - result = new PSObject(); - } + result ??= new PSObject(); result.Properties.Add(new PSNoteProperty(property, value)); } @@ -7254,7 +7241,7 @@ internal static bool PathIsNetworkPath(string path) return false; } - if (Utils.PathIsUnc(path)) + if (Utils.PathIsUnc(path, networkOnly : true)) { return true; } @@ -8067,23 +8054,10 @@ protected override bool ReleaseHandle() private static extern bool FindClose(IntPtr handle); } - [DllImport(PinvokeDllNames.FindFirstFileDllName, EntryPoint = "FindFirstFileExW", SetLastError = true, CharSet = CharSet.Unicode)] - private static extern SafeFindHandle FindFirstFileEx(string lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, ref WIN32_FIND_DATA lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, IntPtr lpSearchFilter, int dwAdditionalFlags); - - internal enum FINDEX_INFO_LEVELS : uint - { - FindExInfoStandard = 0x0u, - FindExInfoBasic = 0x1u, - FindExInfoMaxInfoLevel = 0x2u, - } - - internal enum FINDEX_SEARCH_OPS : uint - { - FindExSearchNameMatch = 0x0u, - FindExSearchLimitToDirectories = 0x1u, - FindExSearchLimitToDevices = 0x2u, - FindExSearchMaxSearchOp = 0x3u, - } + // We use 'FindFirstFileW' instead of 'FindFirstFileExW' because the latter doesn't work correctly with Unicode file names on FAT32. + // See https://github.com/PowerShell/PowerShell/issues/16804 + [DllImport(PinvokeDllNames.FindFirstFileDllName, EntryPoint = "FindFirstFileW", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern SafeFindHandle FindFirstFile(string lpFileName, ref WIN32_FIND_DATA lpFindFileData); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] internal unsafe struct WIN32_FIND_DATA @@ -8105,15 +8079,34 @@ internal unsafe struct WIN32_FIND_DATA /// /// The object of FileInfo or DirectoryInfo type. /// The target of the reparse point. + [Obsolete("This method is now obsolete. Please use the .NET API 'FileSystemInfo.LinkTarget'", error: true)] public static string GetTarget(PSObject instance) { if (instance.BaseObject is FileSystemInfo fileSysInfo) { -#if !UNIX - return WinInternalGetTarget(fileSysInfo.FullName); -#else - return UnixInternalGetTarget(fileSysInfo.FullName); -#endif + if (!fileSysInfo.Exists) + { + throw new ArgumentException( + StringUtil.Format(SessionStateStrings.PathNotFound, fileSysInfo.FullName)); + } + + return fileSysInfo.LinkTarget; + } + + return null; + } + + /// + /// Gets the target for a given file or directory, resolving symbolic links. + /// + /// The FileInfo or DirectoryInfo type. + /// The file path the instance points to. + public static string ResolvedTarget(PSObject instance) + { + if (instance.BaseObject is FileSystemInfo fileSysInfo) + { + FileSystemInfo linkTarget = fileSysInfo.ResolveLinkTarget(true); + return linkTarget is null ? fileSysInfo.FullName : linkTarget.FullName; } return null; @@ -8136,20 +8129,6 @@ public static string GetLinkType(PSObject instance) return null; } -#if UNIX - private static string UnixInternalGetTarget(string filePath) - { - string link = Platform.NonWindowsInternalGetTarget(filePath); - - if (string.IsNullOrEmpty(link)) - { - throw new Win32Exception(Marshal.GetLastWin32Error()); - } - - return link; - } -#endif - private static string InternalGetLinkType(FileSystemInfo fileInfo) { if (Platform.IsWindows) @@ -8165,16 +8144,11 @@ private static string InternalGetLinkType(FileSystemInfo fileInfo) [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] private static string WinInternalGetLinkType(string filePath) { - if (!Platform.IsWindows) - { - throw new PlatformNotSupportedException(); - } - // We set accessMode parameter to zero because documentation says: // If this parameter is zero, the application can query certain metadata // such as file, directory, or device attributes without accessing // that file or device, even if GENERIC_READ access would have been denied. - using (SafeFileHandle handle = OpenReparsePoint(filePath, FileDesiredAccess.GenericZero)) + using (SafeFileHandle handle = WinOpenReparsePoint(filePath, FileDesiredAccess.GenericZero)) { int outBufferSize = Marshal.SizeOf(); @@ -8267,12 +8241,12 @@ internal static bool IsReparsePointLikeSymlink(FileSystemInfo fileInfo) WIN32_FIND_DATA data = default; string fullPath = Path.TrimEndingDirectorySeparator(fileInfo.FullName); - if (fullPath.Length > MAX_PATH) + if (fullPath.Length >= MAX_PATH) { fullPath = PathUtils.EnsureExtendedPrefix(fullPath); } - using (SafeFindHandle handle = FindFirstFileEx(fullPath, FINDEX_INFO_LEVELS.FindExInfoBasic, ref data, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, 0)) + using (SafeFindHandle handle = FindFirstFile(fullPath, ref data)) { if (handle.IsInvalid) { @@ -8439,176 +8413,77 @@ internal static bool WinIsHardLink(ref IntPtr handle) return succeeded && (handleInfo.NumberOfLinks > 1); } -#if !UNIX - internal static string WinInternalGetTarget(string path) - { - // We set accessMode parameter to zero because documentation says: - // If this parameter is zero, the application can query certain metadata - // such as file, directory, or device attributes without accessing - // that file or device, even if GENERIC_READ access would have been denied. - using (SafeFileHandle handle = OpenReparsePoint(path, FileDesiredAccess.GenericZero)) - { - return WinInternalGetTarget(handle); - } - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] - private static string WinInternalGetTarget(SafeFileHandle handle) - { - int outBufferSize = Marshal.SizeOf(); - - IntPtr outBuffer = Marshal.AllocHGlobal(outBufferSize); - bool success = false; - - try - { - int bytesReturned; - - // OACR warning 62001 about using DeviceIOControl has been disabled. - // According to MSDN guidance DangerousAddRef() and DangerousRelease() have been used. - handle.DangerousAddRef(ref success); - - bool result = DeviceIoControl( - handle.DangerousGetHandle(), - FSCTL_GET_REPARSE_POINT, - InBuffer: IntPtr.Zero, - nInBufferSize: 0, - outBuffer, - outBufferSize, - out bytesReturned, - lpOverlapped: IntPtr.Zero); - - if (!result) - { - // It's not a reparse point or the file system doesn't support reparse points. - return null; - } - - string targetDir = null; - - REPARSE_DATA_BUFFER_SYMBOLICLINK reparseDataBuffer = Marshal.PtrToStructure(outBuffer); - - switch (reparseDataBuffer.ReparseTag) - { - case IO_REPARSE_TAG_SYMLINK: - targetDir = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer, reparseDataBuffer.SubstituteNameOffset, reparseDataBuffer.SubstituteNameLength); - break; - - case IO_REPARSE_TAG_MOUNT_POINT: - REPARSE_DATA_BUFFER_MOUNTPOINT reparseMountPointDataBuffer = Marshal.PtrToStructure(outBuffer); - targetDir = Encoding.Unicode.GetString(reparseMountPointDataBuffer.PathBuffer, reparseMountPointDataBuffer.SubstituteNameOffset, reparseMountPointDataBuffer.SubstituteNameLength); - break; - - default: - return null; - } - - if (targetDir != null && targetDir.StartsWith(NonInterpretedPathPrefix, StringComparison.OrdinalIgnoreCase)) - { - targetDir = targetDir.Substring(NonInterpretedPathPrefix.Length); - } - - return targetDir; - } - finally - { - if (success) - { - handle.DangerousRelease(); - } - - Marshal.FreeHGlobal(outBuffer); - } - } -#endif - internal static bool CreateJunction(string path, string target) { - // this is a purely Windows specific feature, no feature flag - // used for that reason + // this is a purely Windows specific feature, no feature flag used for that reason. if (Platform.IsWindows) { return WinCreateJunction(path, target); } - else - { - return false; - } + + return false; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] private static bool WinCreateJunction(string path, string target) { - if (!string.IsNullOrEmpty(path)) + if (string.IsNullOrEmpty(path)) { - if (!string.IsNullOrEmpty(target)) - { - using (SafeHandle handle = OpenReparsePoint(path, FileDesiredAccess.GenericWrite)) - { - byte[] mountPointBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(target)); + throw new ArgumentNullException(nameof(path)); + } - REPARSE_DATA_BUFFER_MOUNTPOINT mountPoint = new REPARSE_DATA_BUFFER_MOUNTPOINT(); - mountPoint.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; - mountPoint.ReparseDataLength = (ushort)(mountPointBytes.Length + 12); // Added space for the header and null endo - mountPoint.SubstituteNameOffset = 0; - mountPoint.SubstituteNameLength = (ushort)mountPointBytes.Length; - mountPoint.PrintNameOffset = (ushort)(mountPointBytes.Length + 2); // 2 as unicode null take 2 bytes. - mountPoint.PrintNameLength = 0; - mountPoint.PathBuffer = new byte[0x3FF0]; // Buffer for max size. - Array.Copy(mountPointBytes, mountPoint.PathBuffer, mountPointBytes.Length); + if (string.IsNullOrEmpty(target)) + { + throw new ArgumentNullException(nameof(target)); + } - int nativeBufferSize = Marshal.SizeOf(mountPoint); - IntPtr nativeBuffer = Marshal.AllocHGlobal(nativeBufferSize); - bool success = false; + using (SafeHandle handle = WinOpenReparsePoint(path, FileDesiredAccess.GenericWrite)) + { + byte[] mountPointBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(target)); - try - { - Marshal.StructureToPtr(mountPoint, nativeBuffer, false); + var mountPoint = new REPARSE_DATA_BUFFER_MOUNTPOINT(); + mountPoint.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + mountPoint.ReparseDataLength = (ushort)(mountPointBytes.Length + 12); // Added space for the header and null endo + mountPoint.SubstituteNameOffset = 0; + mountPoint.SubstituteNameLength = (ushort)mountPointBytes.Length; + mountPoint.PrintNameOffset = (ushort)(mountPointBytes.Length + 2); // 2 as unicode null take 2 bytes. + mountPoint.PrintNameLength = 0; + mountPoint.PathBuffer = new byte[0x3FF0]; // Buffer for max size. + Array.Copy(mountPointBytes, mountPoint.PathBuffer, mountPointBytes.Length); - int bytesReturned = 0; + int nativeBufferSize = Marshal.SizeOf(mountPoint); + IntPtr nativeBuffer = Marshal.AllocHGlobal(nativeBufferSize); + bool success = false; - // OACR warning 62001 about using DeviceIOControl has been disabled. - // According to MSDN guidance DangerousAddRef() and DangerousRelease() have been used. - handle.DangerousAddRef(ref success); + try + { + Marshal.StructureToPtr(mountPoint, nativeBuffer, false); - bool result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT, nativeBuffer, mountPointBytes.Length + 20, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); + int bytesReturned = 0; - if (!result) - { - throw new Win32Exception(Marshal.GetLastWin32Error()); - } + // OACR warning 62001 about using DeviceIOControl has been disabled. + // According to MSDN guidance DangerousAddRef() and DangerousRelease() have been used. + handle.DangerousAddRef(ref success); - return result; - } - finally - { - Marshal.FreeHGlobal(nativeBuffer); + bool result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT, nativeBuffer, mountPointBytes.Length + 20, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero); - if (success) - { - handle.DangerousRelease(); - } - } + if (!result) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); } + + return result; } - else + finally { - throw new ArgumentNullException(nameof(target)); + Marshal.FreeHGlobal(nativeBuffer); + + if (success) + { + handle.DangerousRelease(); + } } } - else - { - throw new ArgumentNullException(nameof(path)); - } - } - - private static SafeFileHandle OpenReparsePoint(string reparsePoint, FileDesiredAccess accessMode) - { -#if UNIX - throw new PlatformNotSupportedException(); -#else - return WinOpenReparsePoint(reparsePoint, accessMode); -#endif } private static SafeFileHandle WinOpenReparsePoint(string reparsePoint, FileDesiredAccess accessMode) @@ -9260,7 +9135,7 @@ function PSRemoteDestinationPathIsFile # Return a hash table in the following format: # DirectoryPath is the directory to be created. - # PathExists is a bool to to keep track of whether the directory already exist. + # PathExists is a bool to keep track of whether the directory already exist. # # 1) If DirectoryPath already exists: # a) If -Force is specified, force create the directory. Set DirectoryPath to the created directory path. diff --git a/src/System.Management.Automation/namespaces/FileSystemSecurity.cs b/src/System.Management.Automation/namespaces/FileSystemSecurity.cs index 97ab38158ae..c886bbd0a0b 100644 --- a/src/System.Management.Automation/namespaces/FileSystemSecurity.cs +++ b/src/System.Management.Automation/namespaces/FileSystemSecurity.cs @@ -222,7 +222,7 @@ private void SetSecurityDescriptor(string path, ObjectSecurity sd, AccessControl // Transfer it to the new file / directory. // We keep these two code branches so that we can have more - // granular information when we ouput the object type via + // granular information when we output the object type via // WriteSecurityDescriptorObject. if (Directory.Exists(path)) { diff --git a/src/System.Management.Automation/namespaces/LocationGlobber.cs b/src/System.Management.Automation/namespaces/LocationGlobber.cs index e4ab1f3cfc5..e5ae0d7b9fd 100644 --- a/src/System.Management.Automation/namespaces/LocationGlobber.cs +++ b/src/System.Management.Automation/namespaces/LocationGlobber.cs @@ -1967,7 +1967,7 @@ CmdletProviderContext context string driveRoot = drive.Root.Replace(StringLiterals.AlternatePathSeparator, StringLiterals.DefaultPathSeparator); driveRoot = driveRoot.TrimEnd(StringLiterals.DefaultPathSeparator); - // Keep on lopping off children until the the remaining path + // Keep on lopping off children until the remaining path // is the drive root. while ((!string.IsNullOrEmpty(providerPath)) && (!providerPath.Equals(driveRoot, StringComparison.OrdinalIgnoreCase))) diff --git a/src/System.Management.Automation/namespaces/NavigationProviderBase.cs b/src/System.Management.Automation/namespaces/NavigationProviderBase.cs index da8a16869d1..cf656e633a3 100644 --- a/src/System.Management.Automation/namespaces/NavigationProviderBase.cs +++ b/src/System.Management.Automation/namespaces/NavigationProviderBase.cs @@ -515,10 +515,7 @@ internal string ContractRelativePath( return string.Empty; } - if (basePath == null) - { - basePath = string.Empty; - } + basePath ??= string.Empty; providerBaseTracer.WriteLine("basePath = {0}", basePath); diff --git a/src/System.Management.Automation/namespaces/RegistryProvider.cs b/src/System.Management.Automation/namespaces/RegistryProvider.cs index 1898c960620..59a398ef1f6 100644 --- a/src/System.Management.Automation/namespaces/RegistryProvider.cs +++ b/src/System.Management.Automation/namespaces/RegistryProvider.cs @@ -61,6 +61,9 @@ namespace Microsoft.PowerShell.Commands [OutputType(typeof(RegistryKey), ProviderCmdlet = ProviderCmdlet.GetItem)] [OutputType(typeof(RegistryKey), typeof(string), typeof(Int32), typeof(Int64), ProviderCmdlet = ProviderCmdlet.GetItemProperty)] [OutputType(typeof(RegistryKey), ProviderCmdlet = ProviderCmdlet.NewItem)] + [OutputType(typeof(string), typeof(PathInfo), ProviderCmdlet = ProviderCmdlet.ResolvePath)] + [OutputType(typeof(PathInfo), ProviderCmdlet = ProviderCmdlet.PushLocation)] + [OutputType(typeof(PathInfo), ProviderCmdlet = ProviderCmdlet.PopLocation)] public sealed partial class RegistryProvider : NavigationCmdletProvider, IPropertyCmdletProvider, @@ -2993,10 +2996,7 @@ private void GetFilteredRegistryKeyProperties(string path, // If properties were not specified, get all the values - if (propertyNames == null) - { - propertyNames = new Collection(); - } + propertyNames ??= new Collection(); if (propertyNames.Count == 0 && getAll) { diff --git a/src/System.Management.Automation/namespaces/TransactedRegistryKey.cs b/src/System.Management.Automation/namespaces/TransactedRegistryKey.cs index dac6f6c0897..443c2547dfa 100644 --- a/src/System.Management.Automation/namespaces/TransactedRegistryKey.cs +++ b/src/System.Management.Automation/namespaces/TransactedRegistryKey.cs @@ -1331,7 +1331,7 @@ public RegistryValueKind GetValueKind(string name) /** * Retrieves the current state of the dirty property. * - * A key is marked as dirty if any operation has occured that modifies the + * A key is marked as dirty if any operation has occurred that modifies the * contents of the key. * * @return true if the key has been modified. diff --git a/src/System.Management.Automation/resources/Authenticode.resx b/src/System.Management.Automation/resources/Authenticode.resx index c196f2b0d65..d180743c34d 100644 --- a/src/System.Management.Automation/resources/Authenticode.resx +++ b/src/System.Management.Automation/resources/Authenticode.resx @@ -142,7 +142,7 @@ Cannot sign code. The specified certificate is not suitable for code signing. - Cannot sign code. The TimeStamp server URL must be fully qualified, and in the format http://<server url>. + Cannot sign code. The TimeStamp server URL must be fully qualified, and in the format http://<server url> or https://<server url>. Cannot sign code. The hash algorithm is not supported. diff --git a/src/System.Management.Automation/resources/CommandBaseStrings.resx b/src/System.Management.Automation/resources/CommandBaseStrings.resx index 656dc226f22..abb918adb16 100644 --- a/src/System.Management.Automation/resources/CommandBaseStrings.resx +++ b/src/System.Management.Automation/resources/CommandBaseStrings.resx @@ -156,6 +156,15 @@ Pause the current pipeline and return to the command prompt. Type "{0}" to resume the pipeline. + + + Program "{0}" ended with non-zero exit code: {1}. + Performing the operation "{0}" on target "{1}". @@ -213,4 +222,10 @@ Reviewed by TArcher on 2010-07-20 The {0} is obsolete. {1} + + Exec call failed with errorno {0} for command line: {1} + + + Command '{0}' was not found. The specified command must be an executable. + diff --git a/src/System.Management.Automation/resources/ConsoleInfoErrorStrings.resx b/src/System.Management.Automation/resources/ConsoleInfoErrorStrings.resx index 201dde0e4ac..ba0809e9010 100644 --- a/src/System.Management.Automation/resources/ConsoleInfoErrorStrings.resx +++ b/src/System.Management.Automation/resources/ConsoleInfoErrorStrings.resx @@ -234,4 +234,10 @@ The Save operation failed. Cannot remove the file {0}. + + The provided configuration file '{0}' does not exist. + + + The provided configuration file '{0}' must have a .pssc file extension. + diff --git a/src/System.Management.Automation/resources/DiscoveryExceptions.resx b/src/System.Management.Automation/resources/DiscoveryExceptions.resx index d3923969e8d..2d8445deb4f 100644 --- a/src/System.Management.Automation/resources/DiscoveryExceptions.resx +++ b/src/System.Management.Automation/resources/DiscoveryExceptions.resx @@ -206,6 +206,10 @@ The #requires statement must be in one of the following formats: The '{0}' command was found in the module '{1}', but the module could not be loaded. For more information, run 'Import-Module {1}'. + + The '{0}' command was found in the module '{1}', but the module could not be loaded due to the following error: [{2}] +For more information, run 'Import-Module {1}'. + The module '{0}' could not be loaded. For more information, run 'Import-Module {0}'. diff --git a/src/System.Management.Automation/resources/ExtendedTypeSystem.resx b/src/System.Management.Automation/resources/ExtendedTypeSystem.resx index 6a697cba888..3a84b103102 100644 --- a/src/System.Management.Automation/resources/ExtendedTypeSystem.resx +++ b/src/System.Management.Automation/resources/ExtendedTypeSystem.resx @@ -156,6 +156,9 @@ Cannot find an overload for "{0}" and the argument count: "{1}". + + Could not find a suitable generic method overload for "{0}" with "{1}" type parameters, and the argument count: "{2}". + Multiple ambiguous overloads found for "{0}" and the argument count: "{1}". @@ -349,7 +352,10 @@ "{0}" returned a null value. - The {0} property was not found for the {1} object. The available property is: {2} + The property '{0}' was not found for the '{1}' object. The settable properties are: {2}. + + + The property '{0}' was not found for the '{1}' object. There is no settable property available. Cannot create object of type "{0}". {1} diff --git a/src/System.Management.Automation/resources/FileSystemProviderStrings.resx b/src/System.Management.Automation/resources/FileSystemProviderStrings.resx index 1b4d1159e59..9c232ae836e 100644 --- a/src/System.Management.Automation/resources/FileSystemProviderStrings.resx +++ b/src/System.Management.Automation/resources/FileSystemProviderStrings.resx @@ -345,4 +345,7 @@ Skip already-visited directory {0}. + + Destination path cannot be a subdirectory of the source: {0}. + diff --git a/src/System.Management.Automation/resources/Modules.resx b/src/System.Management.Automation/resources/Modules.resx index 345811502b6..2e50a3c5317 100644 --- a/src/System.Management.Automation/resources/Modules.resx +++ b/src/System.Management.Automation/resources/Modules.resx @@ -624,4 +624,7 @@ Cannot create new module while the session is in ConstrainedLanguage mode. + + Cannot find the built-in module '{0}' that is compatible with the 'Core' edition. Please make sure the PowerShell built-in modules are available. They usually come with the PowerShell package under the $PSHOME module path, and are required for PowerShell to function properly. + diff --git a/src/System.Management.Automation/resources/ParserStrings.resx b/src/System.Management.Automation/resources/ParserStrings.resx index d21719157ac..c1ae5c6ada4 100644 --- a/src/System.Management.Automation/resources/ParserStrings.resx +++ b/src/System.Management.Automation/resources/ParserStrings.resx @@ -296,6 +296,9 @@ Possible matches are Unable to index into an object of type "{0}" with the ByRef-like return type "{1}". ByRef-like types are not supported in PowerShell. + + The array has too many dimensions: {0}. The number of dimensions for an array must be less than or equal to 32. + Array assignment to [{0}] failed: {1}. @@ -497,7 +500,7 @@ The correct form is: foreach ($a in $b) {...} Script command clause '{0}' has already been defined. - unexpected token '{0}', expected 'begin', 'process', 'end', or 'dynamicparam'. + unexpected token '{0}', expected 'begin', 'process', 'end', 'clean', or 'dynamicparam'. Missing closing '}' in statement block or type definition. @@ -1126,6 +1129,9 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent Unable to convert input to the target type [{0}] passed to the ForEach() operator. Please check the specified type and try running your script again. + + Script block with a 'clean' block is not supported by the 'ForEach' method. + The 'numberToReturn' value provided to the third argument of the Where() operator must be greater than zero. Please correct the argument's value and try running your script again. @@ -1479,4 +1485,7 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent Background operators can only be used at the end of a pipeline chain. + + Directly invoking the 'clean' block of a script block is not supported. + diff --git a/src/System.Management.Automation/resources/PathUtilsStrings.resx b/src/System.Management.Automation/resources/PathUtilsStrings.resx index 217f2522ac8..07a4677fa2f 100644 --- a/src/System.Management.Automation/resources/PathUtilsStrings.resx +++ b/src/System.Management.Automation/resources/PathUtilsStrings.resx @@ -138,6 +138,9 @@ The directory '{0}' already exists. Use the -Force parameter if you want to overwrite the directory and files within the directory. + + The user module path does not exist, and hence a module folder cannot be created for the provided module name '{0}'. + Cannot create the module {0} due to the following: {1}. Use a different argument for the -OutputModule parameter and retry. {StrContains="OutputModule"} diff --git a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx index 441228c9c44..57d2a22a0a6 100644 --- a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx +++ b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx @@ -837,8 +837,7 @@ Note that 'Start-Job' is not supported by design in scenarios where PowerShell i A {1} job source adapter threw an exception with the following message: {0} - The value {0} is not valid for the {1} parameter. The available values are 2.0, 3.0, 4.0, 5.0, 5.1. - {StrContains="2.0"} {StrContains="3.0"} + The value {0} is not valid for the {1} parameter. The only allowed value is 5.1. The Wait and Keep parameters cannot be used together in the same command. @@ -846,8 +845,8 @@ Note that 'Start-Job' is not supported by design in scenarios where PowerShell i The WriteEvents parameter cannot be used without the Wait parameter. - - PowerShell {0} is not installed. Install PowerShell {0}, and then try again. + + PowerShell remoting endpoint versioning is not supported on PowerShell Core. The following type cannot be instantiated because its constructor is not public: {0}. @@ -1409,7 +1408,7 @@ All WinRM sessions connected to PowerShell session configurations, such as Micro Multiple processes were found with this name {0}. Use the process Id to specify a single process to enter. - Cannot enter process {0} because it has not loaded the PowerShell engine. + Cannot enter process with Id '{0}' because it has not loaded the PowerShell engine. No process was found with Id: {0}. @@ -1703,4 +1702,16 @@ SSH client process terminated before connection could be established. Unable to create Windows PowerShell process because Windows PowerShell could not be found on this machine. + + The Runspace argument to Create must be a non-null RemoteRunspace object. + + + The session configuration hash table contains an invalid key type. Keys should be string types. + + + The session configuration file contains an unsupported configuration option: {0}. This is a remoting endpoint configuration option, that does not apply to PowerShell session state. + + + The session configuration file contains an unknown configuration option: {0}. + diff --git a/src/System.Management.Automation/resources/RunspaceInit.resx b/src/System.Management.Automation/resources/RunspaceInit.resx index a2497961901..f12ba3f71c5 100644 --- a/src/System.Management.Automation/resources/RunspaceInit.resx +++ b/src/System.Management.Automation/resources/RunspaceInit.resx @@ -186,6 +186,9 @@ Dictates what type of prompt should be displayed for the current nesting level + + If true, $ErrorActionPreference applies to native executables, so that non-zero exit codes will generate cmdlet-style errors governed by error action settings + If true, WhatIf is considered to be enabled for all commands. diff --git a/src/System.Management.Automation/resources/SecuritySupportStrings.resx b/src/System.Management.Automation/resources/SecuritySupportStrings.resx index cd8a65a5c90..cf1ca357cc4 100644 --- a/src/System.Management.Automation/resources/SecuritySupportStrings.resx +++ b/src/System.Management.Automation/resources/SecuritySupportStrings.resx @@ -153,4 +153,10 @@ Invalid session key data. + + Script file, '{0}', is blocked from running by system policy. + + + An unknown script file policy enforcement value was returned: {0}. + diff --git a/src/System.Management.Automation/resources/SessionStateStrings.resx b/src/System.Management.Automation/resources/SessionStateStrings.resx index 39dd766d749..f5d817ccd02 100644 --- a/src/System.Management.Automation/resources/SessionStateStrings.resx +++ b/src/System.Management.Automation/resources/SessionStateStrings.resx @@ -457,7 +457,7 @@ Cannot find alias because alias '{0}' does not exist. - Cannot set the location because path '{0}' resolved to multiple containers. You can only the set location to a single container at a time. + Cannot set the location because path '{0}' resolved to multiple containers. You can only set the location to a single container at a time. Cannot process variable because variable path '{0}' resolved to multiple items. You can get or set the variable value only one item at a time. diff --git a/src/System.Management.Automation/security/Authenticode.cs b/src/System.Management.Automation/security/Authenticode.cs index 70b6778c37e..1906d18bf6b 100644 --- a/src/System.Management.Automation/security/Authenticode.cs +++ b/src/System.Management.Automation/security/Authenticode.cs @@ -4,12 +4,16 @@ #pragma warning disable 1634, 1691 #pragma warning disable 56523 -using Dbg = System.Management.Automation; +#if !UNIX +using Microsoft.Security.Extensions; +#endif using System.IO; using System.Management.Automation.Internal; using System.Management.Automation.Security; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; + +using Dbg = System.Management.Automation; using DWORD = System.UInt32; namespace System.Management.Automation @@ -105,11 +109,12 @@ internal static Signature SignFile(SigningOption option, Utils.CheckArgForNullOrEmpty(fileName, "fileName"); Utils.CheckArgForNull(certificate, "certificate"); - // If given, TimeStamp server URLs must begin with http:// + // If given, TimeStamp server URLs must begin with http:// or https:// if (!string.IsNullOrEmpty(timeStampServerUrl)) { - if ((timeStampServerUrl.Length <= 7) || - (timeStampServerUrl.IndexOf("http://", StringComparison.OrdinalIgnoreCase) != 0)) + if ((timeStampServerUrl.Length <= 7) || ( + (timeStampServerUrl.IndexOf("http://", StringComparison.OrdinalIgnoreCase) != 0) && + (timeStampServerUrl.IndexOf("https://", StringComparison.OrdinalIgnoreCase) != 0))) { throw PSTraceSource.NewArgumentException( nameof(certificate), @@ -275,12 +280,12 @@ internal static Signature GetSignature(string fileName, string fileContent) if (fileContent == null) { - // First, try to get the signature from the catalog signature APIs. - signature = GetSignatureFromCatalog(fileName); + // First, try to get the signature from the latest dotNet signing API. + signature = GetSignatureFromMSSecurityExtensions(fileName); } // If there is no signature or it is invalid, go by the file content - // with the older WinVerifyTrust APIs + // with the older WinVerifyTrust APIs. if ((signature == null) || (signature.Status != SignatureStatus.Valid)) { signature = GetSignatureFromWinVerifyTrust(fileName, fileContent); @@ -289,147 +294,108 @@ internal static Signature GetSignature(string fileName, string fileContent) return signature; } + /// + /// Gets the file signature using the dotNet Microsoft.Security.Extensions package. + /// This supports both Windows catalog file signatures and embedded file signatures. + /// But it is not supported on all Windows platforms/skus, noteably Win7 and nanoserver. + /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")] - private static Signature GetSignatureFromCatalog(string filename) + private static Signature GetSignatureFromMSSecurityExtensions(string filename) { +#if UNIX + return null; +#else if (Signature.CatalogApiAvailable.HasValue && !Signature.CatalogApiAvailable.Value) { - // Signature.CatalogApiAvailable would be set to false the first time it is detected that - // WTGetSignatureInfo API does not exist on the platform, or if the API is not functional on the target platform. - // Just return from the function instead of revalidating. return null; } - Signature signature = null; - Utils.CheckArgForNullOrEmpty(filename, "fileName"); SecuritySupport.CheckIfFileExists(filename); - try + Signature signature = null; + FileSignatureInfo fileSigInfo; + using (FileStream fileStream = File.OpenRead(filename)) { - using (FileStream stream = File.OpenRead(filename)) + try + { + fileSigInfo = FileSignatureInfo.GetFromFileStream(fileStream); + System.Diagnostics.Debug.Assert(fileSigInfo is not null, "Returned FileSignatureInfo should never be null."); + } + catch (Exception) { - NativeMethods.SIGNATURE_INFO sigInfo = new NativeMethods.SIGNATURE_INFO(); - sigInfo.cbSize = (uint)Marshal.SizeOf(sigInfo); + // For any API error, enable fallback to WinVerifyTrust APIs. + Signature.CatalogApiAvailable = false; + return null; + } + } - IntPtr ppCertContext = IntPtr.Zero; - IntPtr phStateData = IntPtr.Zero; + DWORD error = GetErrorFromSignatureState(fileSigInfo.State); - try - { - int hresult = NativeMethods.WTGetSignatureInfo(filename, stream.SafeFileHandle.DangerousGetHandle(), - NativeMethods.SIGNATURE_INFO_FLAGS.SIF_CATALOG_SIGNED | - NativeMethods.SIGNATURE_INFO_FLAGS.SIF_CATALOG_FIRST | - NativeMethods.SIGNATURE_INFO_FLAGS.SIF_AUTHENTICODE_SIGNED | - NativeMethods.SIGNATURE_INFO_FLAGS.SIF_BASE_VERIFICATION | - NativeMethods.SIGNATURE_INFO_FLAGS.SIF_CHECK_OS_BINARY, - ref sigInfo, ref ppCertContext, ref phStateData); - - if (Utils.Succeeded(hresult)) - { - DWORD error = GetErrorFromSignatureState(sigInfo.nSignatureState); - - X509Certificate2 cert = null; - - if (ppCertContext != IntPtr.Zero) - { - cert = new X509Certificate2(ppCertContext); - - // Get the time stamper certificate if available - TryGetProviderSigner(phStateData, out IntPtr pProvSigner, out X509Certificate2 timestamperCert); - if (timestamperCert != null) - { - signature = new Signature(filename, error, cert, timestamperCert); - } - else - { - signature = new Signature(filename, error, cert); - } - - switch (sigInfo.nSignatureType) - { - case NativeMethods.SIGNATURE_INFO_TYPE.SIT_AUTHENTICODE: signature.SignatureType = SignatureType.Authenticode; break; - case NativeMethods.SIGNATURE_INFO_TYPE.SIT_CATALOG: signature.SignatureType = SignatureType.Catalog; break; - } - - if (sigInfo.fOSBinary == 1) - { - signature.IsOSBinary = true; - } - } - else - { - signature = new Signature(filename, error); - } - - if (!Signature.CatalogApiAvailable.HasValue) - { - string productFile = Path.Combine(Utils.DefaultPowerShellAppBase, "Modules\\PSDiagnostics\\PSDiagnostics.psm1"); - if (signature.Status != SignatureStatus.Valid) - { - if (string.Equals(filename, productFile, StringComparison.OrdinalIgnoreCase)) - { - Signature.CatalogApiAvailable = false; - } - else - { - // ProductFile has to be Catalog signed. Hence validating - // to see if the Catalog API is functional using the ProductFile. - Signature productFileSignature = GetSignatureFromCatalog(productFile); - Signature.CatalogApiAvailable = (productFileSignature != null && productFileSignature.Status == SignatureStatus.Valid); - } - } - } - } - else - { - // If calling NativeMethods.WTGetSignatureInfo failed (returned a non-zero value), we still want to set Signature.CatalogApiAvailable to false. - Signature.CatalogApiAvailable = false; - } - } - finally - { - if (phStateData != IntPtr.Zero) - { - NativeMethods.FreeWVTStateData(phStateData); - } + if (fileSigInfo.SigningCertificate is null) + { + signature = new Signature(filename, error); + } + else + { + signature = fileSigInfo.TimestampCertificate is null ? + new Signature(filename, error, fileSigInfo.SigningCertificate) : + new Signature(filename, error, fileSigInfo.SigningCertificate, fileSigInfo.TimestampCertificate); + } - if (ppCertContext != IntPtr.Zero) - { - NativeMethods.CertFreeCertificateContext(ppCertContext); - } - } - } + switch (fileSigInfo.Kind) + { + case SignatureKind.None: + signature.SignatureType = SignatureType.None; + break; + + case SignatureKind.Embedded: + signature.SignatureType = SignatureType.Authenticode; + break; + + case SignatureKind.Catalog: + signature.SignatureType = SignatureType.Catalog; + break; + + default: + System.Diagnostics.Debug.Fail("Signature type can only be None, Authenticode or Catalog."); + break; } - catch (TypeLoadException) + + signature.IsOSBinary = fileSigInfo.IsOSBinary; + + if (signature.SignatureType == SignatureType.Catalog && !Signature.CatalogApiAvailable.HasValue) { - // If we don't have WTGetSignatureInfo, don't return a Signature. - Signature.CatalogApiAvailable = false; - return null; + Signature.CatalogApiAvailable = fileSigInfo.State != SignatureState.Invalid; } return signature; +#endif } - private static DWORD GetErrorFromSignatureState(NativeMethods.SIGNATURE_STATE state) +#if !UNIX + private static DWORD GetErrorFromSignatureState(SignatureState signatureState) { - switch (state) + switch (signatureState) { - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_UNSIGNED_MISSING: return Win32Errors.TRUST_E_NOSIGNATURE; - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_UNSIGNED_UNSUPPORTED: return Win32Errors.TRUST_E_NOSIGNATURE; - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_UNSIGNED_POLICY: return Win32Errors.TRUST_E_NOSIGNATURE; - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_INVALID_CORRUPT: return Win32Errors.TRUST_E_BAD_DIGEST; - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_INVALID_POLICY: return Win32Errors.CRYPT_E_BAD_MSG; - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_VALID: return Win32Errors.NO_ERROR; - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_TRUSTED: return Win32Errors.NO_ERROR; - case NativeMethods.SIGNATURE_STATE.SIGNATURE_STATE_UNTRUSTED: return Win32Errors.TRUST_E_EXPLICIT_DISTRUST; - - // Should not happen + case SignatureState.Unsigned: + return Win32Errors.TRUST_E_NOSIGNATURE; + + case SignatureState.SignedAndTrusted: + return Win32Errors.NO_ERROR; + + case SignatureState.SignedAndNotTrusted: + return Win32Errors.TRUST_E_EXPLICIT_DISTRUST; + + case SignatureState.Invalid: + return Win32Errors.TRUST_E_BAD_DIGEST; + default: - System.Diagnostics.Debug.Fail("Should not get here - could not map SIGNATURE_STATE"); + System.Diagnostics.Debug.Fail("Should not get here - could not map FileSignatureInfo.State"); return Win32Errors.TRUST_E_NOSIGNATURE; } } +#endif private static Signature GetSignatureFromWinVerifyTrust(string fileName, string fileContent) { diff --git a/src/System.Management.Automation/security/MshSignature.cs b/src/System.Management.Automation/security/MshSignature.cs index 96ff8a9638a..fd8dd4f67ef 100644 --- a/src/System.Management.Automation/security/MshSignature.cs +++ b/src/System.Management.Automation/security/MshSignature.cs @@ -110,7 +110,7 @@ public sealed class Signature // Three states: // - True: we can rely on the catalog API to check catalog signature. - // - False: we cannot rely on the catalog API, either because it doesn't exist in the OS (win7), + // - False: we cannot rely on the catalog API, either because it doesn't exist in the OS (win7, nano), // or it's not working properly (OneCore SKUs or dev environment where powershell might // be updated/refreshed). // - Null: it's not determined yet whether catalog API can be relied on or not. diff --git a/src/System.Management.Automation/security/SecureStringHelper.cs b/src/System.Management.Automation/security/SecureStringHelper.cs index ddb94e52ee8..4eaab568050 100644 --- a/src/System.Management.Automation/security/SecureStringHelper.cs +++ b/src/System.Management.Automation/security/SecureStringHelper.cs @@ -258,10 +258,7 @@ internal static EncryptionResult Encrypt(SecureString input, byte[] key, byte[] // using (Aes aes = Aes.Create()) { - if (iv is null) - { - iv = aes.IV; - } + iv ??= aes.IV; // // get clear text data from the input SecureString diff --git a/src/System.Management.Automation/security/SecurityManager.cs b/src/System.Management.Automation/security/SecurityManager.cs index c82b03b99c7..25fb544507b 100644 --- a/src/System.Management.Automation/security/SecurityManager.cs +++ b/src/System.Management.Automation/security/SecurityManager.cs @@ -328,9 +328,10 @@ private bool CheckPolicy(ExternalScriptInfo script, PSHost host, out Exception r if (string.Equals(fi.Extension, ".ps1xml", StringComparison.OrdinalIgnoreCase)) { string[] trustedDirectories = new string[] - { Platform.GetFolderPath(Environment.SpecialFolder.System), - Platform.GetFolderPath(Environment.SpecialFolder.ProgramFiles) - }; + { + Environment.GetFolderPath(Environment.SpecialFolder.System), + Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + }; foreach (string trustedDirectory in trustedDirectories) { diff --git a/src/System.Management.Automation/security/SecuritySupport.cs b/src/System.Management.Automation/security/SecuritySupport.cs index e41b64db625..03cf24d80f3 100644 --- a/src/System.Management.Automation/security/SecuritySupport.cs +++ b/src/System.Management.Automation/security/SecuritySupport.cs @@ -412,7 +412,7 @@ public static bool IsProductBinary(string file) return true; } - // WTGetSignatureInfo is used to verify catalog signature. + // WTGetSignatureInfo, via Microsoft.Security.Extensions, is used to verify catalog signature. // On Win7, catalog API is not available. // On OneCore SKUs like NanoServer/IoT, the API has a bug that makes it not able to find the // corresponding catalog file for a given product file, so it doesn't work properly. @@ -1213,7 +1213,7 @@ private void ResolveFromStoreById(ResolutionPurpose purpose, out ErrorRecord err storeCU.Open(OpenFlags.ReadOnly); X509Certificate2Collection storeCerts = storeCU.Certificates; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (Platform.IsWindows) { using (var storeLM = new X509Store("my", StoreLocation.LocalMachine)) { @@ -1383,7 +1383,10 @@ internal static AmsiNativeMethods.AMSI_RESULT ScanContent(string content, string #endif } - internal static AmsiNativeMethods.AMSI_RESULT WinScanContent(string content, string sourceMetadata, bool warmUp) + internal static AmsiNativeMethods.AMSI_RESULT WinScanContent( + string content, + string sourceMetadata, + bool warmUp) { if (string.IsNullOrEmpty(sourceMetadata)) { @@ -1414,33 +1417,9 @@ internal static AmsiNativeMethods.AMSI_RESULT WinScanContent(string content, str try { - int hr = 0; - - // Initialize AntiMalware Scan Interface, if not already initialized. - // If we failed to initialize previously, just return the neutral result ("AMSI_RESULT_NOT_DETECTED") - if (s_amsiContext == IntPtr.Zero) + if (!CheckAmsiInit()) { - hr = Init(); - - if (!Utils.Succeeded(hr)) - { - s_amsiInitFailed = true; - return AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_NOT_DETECTED; - } - } - - // Initialize the session, if one isn't already started. - // If we failed to initialize previously, just return the neutral result ("AMSI_RESULT_NOT_DETECTED") - if (s_amsiSession == IntPtr.Zero) - { - hr = AmsiNativeMethods.AmsiOpenSession(s_amsiContext, ref s_amsiSession); - AmsiInitialized = true; - - if (!Utils.Succeeded(hr)) - { - s_amsiInitFailed = true; - return AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_NOT_DETECTED; - } + return AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_NOT_DETECTED; } if (warmUp) @@ -1453,6 +1432,7 @@ internal static AmsiNativeMethods.AMSI_RESULT WinScanContent(string content, str AmsiNativeMethods.AMSI_RESULT result = AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_CLEAN; // Run AMSI content scan + int hr; unsafe { fixed (char* buffer = content) @@ -1484,6 +1464,123 @@ internal static AmsiNativeMethods.AMSI_RESULT WinScanContent(string content, str } } + /// + /// Reports provided content to AMSI (Antimalware Scan Interface). + /// + /// Name of content being reported. + /// Content being reported. + /// True if content was successfully reported. + internal static bool ReportContent( + string name, + string content) + { +#if UNIX + return false; +#else + return WinReportContent(name, content); +#endif + } + + private static bool WinReportContent( + string name, + string content) + { + if (string.IsNullOrEmpty(name) || + string.IsNullOrEmpty(content) || + s_amsiInitFailed || + s_amsiNotifyFailed) + { + return false; + } + + lock (s_amsiLockObject) + { + if (s_amsiNotifyFailed) + { + return false; + } + + try + { + if (!CheckAmsiInit()) + { + return false; + } + + int hr; + AmsiNativeMethods.AMSI_RESULT result = AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_NOT_DETECTED; + unsafe + { + fixed (char* buffer = content) + { + var buffPtr = new IntPtr(buffer); + hr = AmsiNativeMethods.AmsiNotifyOperation( + amsiContext: s_amsiContext, + buffer: buffPtr, + length: (uint)(content.Length * sizeof(char)), + contentName: name, + ref result); + } + } + + if (Utils.Succeeded(hr)) + { + if (result == AmsiNativeMethods.AMSI_RESULT.AMSI_RESULT_DETECTED) + { + // If malware is detected, throw to prevent method invoke expression from running. + throw new PSSecurityException(ParserStrings.ScriptContainedMaliciousContent); + } + + return true; + } + + return false; + } + catch (DllNotFoundException) + { + s_amsiNotifyFailed = true; + return false; + } + catch (System.EntryPointNotFoundException) + { + s_amsiNotifyFailed = true; + return false; + } + } + } + + private static bool CheckAmsiInit() + { + // Initialize AntiMalware Scan Interface, if not already initialized. + // If we failed to initialize previously, just return the neutral result ("AMSI_RESULT_NOT_DETECTED") + if (s_amsiContext == IntPtr.Zero) + { + int hr = Init(); + + if (!Utils.Succeeded(hr)) + { + s_amsiInitFailed = true; + return false; + } + } + + // Initialize the session, if one isn't already started. + // If we failed to initialize previously, just return the neutral result ("AMSI_RESULT_NOT_DETECTED") + if (s_amsiSession == IntPtr.Zero) + { + int hr = AmsiNativeMethods.AmsiOpenSession(s_amsiContext, ref s_amsiSession); + AmsiInitialized = true; + + if (!Utils.Succeeded(hr)) + { + s_amsiInitFailed = true; + return false; + } + } + + return true; + } + internal static void CurrentDomain_ProcessExit(object sender, EventArgs e) { if (AmsiInitialized && !AmsiUninitializeCalled) @@ -1499,6 +1596,7 @@ internal static void CurrentDomain_ProcessExit(object sender, EventArgs e) private static IntPtr s_amsiSession = IntPtr.Zero; private static bool s_amsiInitFailed = false; + private static bool s_amsiNotifyFailed = false; private static readonly object s_amsiLockObject = new object(); /// @@ -1623,8 +1721,27 @@ internal static extern int AmsiInitialize( [DefaultDllImportSearchPathsAttribute(DllImportSearchPath.System32)] [DllImportAttribute("amsi.dll", EntryPoint = "AmsiScanBuffer", CallingConvention = CallingConvention.StdCall)] internal static extern int AmsiScanBuffer( - System.IntPtr amsiContext, System.IntPtr buffer, uint length, - [InAttribute()][MarshalAsAttribute(UnmanagedType.LPWStr)] string contentName, System.IntPtr amsiSession, ref AMSI_RESULT result); + System.IntPtr amsiContext, + System.IntPtr buffer, + uint length, + [InAttribute()][MarshalAsAttribute(UnmanagedType.LPWStr)] string contentName, + System.IntPtr amsiSession, + ref AMSI_RESULT result); + + /// Return Type: HRESULT->LONG->int + /// amsiContext: HAMSICONTEXT->HAMSICONTEXT__* + /// buffer: PVOID->void* + /// length: ULONG->unsigned int + /// contentName: LPCWSTR->WCHAR* + /// result: AMSI_RESULT* + [DefaultDllImportSearchPathsAttribute(DllImportSearchPath.System32)] + [DllImportAttribute("amsi.dll", EntryPoint = "AmsiNotifyOperation", CallingConvention = CallingConvention.StdCall)] + internal static extern int AmsiNotifyOperation( + System.IntPtr amsiContext, + System.IntPtr buffer, + uint length, + [InAttribute()][MarshalAsAttribute(UnmanagedType.LPWStr)] string contentName, + ref AMSI_RESULT result); /// Return Type: HRESULT->LONG->int ///amsiContext: HAMSICONTEXT->HAMSICONTEXT__* diff --git a/src/System.Management.Automation/security/nativeMethods.cs b/src/System.Management.Automation/security/nativeMethods.cs index 165c4063003..69ed655963d 100644 --- a/src/System.Management.Automation/security/nativeMethods.cs +++ b/src/System.Management.Automation/security/nativeMethods.cs @@ -1145,62 +1145,6 @@ internal static extern DWORD idxCert ); - /// Return Type: HRESULT->LONG->int - ///pszFile: PCWSTR->WCHAR* - ///hFile: HANDLE->void* - ///sigInfoFlags: SIGNATURE_INFO_FLAGS->Anonymous_5157c654_2076_48e7_9241_84ac648615e9 - ///psiginfo: SIGNATURE_INFO* - ///ppCertContext: void** - ///phWVTStateData: HANDLE* - [DllImportAttribute("wintrust.dll", EntryPoint = "WTGetSignatureInfo", CallingConvention = CallingConvention.StdCall)] - internal static extern int WTGetSignatureInfo([InAttribute()][MarshalAsAttribute(UnmanagedType.LPWStr)] string pszFile, [InAttribute()] System.IntPtr hFile, SIGNATURE_INFO_FLAGS sigInfoFlags, ref SIGNATURE_INFO psiginfo, ref System.IntPtr ppCertContext, ref System.IntPtr phWVTStateData); - - internal static void FreeWVTStateData(System.IntPtr phWVTStateData) - { - WINTRUST_DATA wtd = new WINTRUST_DATA(); - DWORD dwResult = Win32Errors.E_FAIL; - IntPtr WINTRUST_ACTION_GENERIC_VERIFY_V2 = IntPtr.Zero; - IntPtr wtdBuffer = IntPtr.Zero; - - Guid actionVerify = - new Guid("00AAC56B-CD44-11d0-8CC2-00C04FC295EE"); - - try - { - WINTRUST_ACTION_GENERIC_VERIFY_V2 = - Marshal.AllocCoTaskMem(Marshal.SizeOf(actionVerify)); - Marshal.StructureToPtr(actionVerify, - WINTRUST_ACTION_GENERIC_VERIFY_V2, - false); - - wtd.cbStruct = (DWORD)Marshal.SizeOf(wtd); - wtd.dwUIChoice = (DWORD)WintrustUIChoice.WTD_UI_NONE; - wtd.fdwRevocationChecks = 0; - wtd.dwUnionChoice = (DWORD)WintrustUnionChoice.WTD_CHOICE_BLOB; - wtd.dwStateAction = (DWORD)WintrustAction.WTD_STATEACTION_CLOSE; - wtd.hWVTStateData = phWVTStateData; - - wtdBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(wtd)); - Marshal.StructureToPtr(wtd, wtdBuffer, false); - - // The GetLastWin32Error of this is checked, but PreSharp doesn't seem to be - // able to see that. -#pragma warning disable 56523 - dwResult = WinVerifyTrust( - IntPtr.Zero, - WINTRUST_ACTION_GENERIC_VERIFY_V2, - wtdBuffer); -#pragma warning restore 56523 - } - finally - { - Marshal.DestroyStructure(wtdBuffer); - Marshal.FreeCoTaskMem(wtdBuffer); - Marshal.DestroyStructure(WINTRUST_ACTION_GENERIC_VERIFY_V2); - Marshal.FreeCoTaskMem(WINTRUST_ACTION_GENERIC_VERIFY_V2); - } - } - // // stuff required for getting cert extensions // diff --git a/src/System.Management.Automation/security/wldpNativeMethods.cs b/src/System.Management.Automation/security/wldpNativeMethods.cs index f8a78f12a9a..28655c1afe8 100644 --- a/src/System.Management.Automation/security/wldpNativeMethods.cs +++ b/src/System.Management.Automation/security/wldpNativeMethods.cs @@ -12,6 +12,32 @@ namespace System.Management.Automation.Security { + /// + /// System wide policy enforcement for a specific script file. + /// + public enum SystemScriptFileEnforcement + { + /// + /// No policy enforcement. + /// + None = 0, + + /// + /// Script file is blocked from running. + /// + Block = 1, + + /// + /// Script file is allowed to run without restrictions (FullLanguage mode). + /// + Allow = 2, + + /// + /// Script file is allowed to run in ConstrainedLanguage mode only. + /// + AllowConstrained = 3 + } + /// /// How the policy is being enforced. /// @@ -51,10 +77,7 @@ public static SystemEnforcementMode GetSystemLockdownPolicy() { lock (s_systemLockdownPolicyLock) { - if (s_systemLockdownPolicy == null) - { - s_systemLockdownPolicy = GetLockdownPolicy(path: null, handle: null); - } + s_systemLockdownPolicy ??= GetLockdownPolicy(path: null, handle: null); } } else if (s_allowDebugOverridePolicy) @@ -71,6 +94,90 @@ public static SystemEnforcementMode GetSystemLockdownPolicy() private static readonly object s_systemLockdownPolicyLock = new object(); private static SystemEnforcementMode? s_systemLockdownPolicy = null; private static bool s_allowDebugOverridePolicy = false; + private static bool s_wldpCanExecuteAvailable = true; + + /// + /// Gets the system wide script file policy enforcement for an open file. + /// Based on system WDAC (Windows Defender Application Control) or AppLocker policies. + /// + /// Script file path for policy check. + /// FileStream object to script file path. + /// Policy check result for script file. + public static SystemScriptFileEnforcement GetFilePolicyEnforcement( + string filePath, + System.IO.FileStream fileStream) + { + SafeHandle fileHandle = fileStream.SafeFileHandle; + + // First check latest WDAC APIs if available. Also revert to legacy APIs if debug hook is in effect. + if (s_wldpCanExecuteAvailable && !s_allowDebugOverridePolicy) + { + try + { + string fileName = System.IO.Path.GetFileNameWithoutExtension(filePath); + string auditMsg = $"PowerShell ExternalScriptInfo reading file: {fileName}"; + + int hr = WldpNativeMethods.WldpCanExecuteFile( + host: PowerShellHost, + options: WLDP_EXECUTION_EVALUATION_OPTIONS.WLDP_EXECUTION_EVALUATION_OPTION_NONE, + fileHandle: fileHandle.DangerousGetHandle(), + auditInfo: auditMsg, + result: out WLDP_EXECUTION_POLICY canExecuteResult); + + if (hr >= 0) + { + switch (canExecuteResult) + { + case WLDP_EXECUTION_POLICY.WLDP_CAN_EXECUTE_ALLOWED: + return SystemScriptFileEnforcement.Allow; + + case WLDP_EXECUTION_POLICY.WLDP_CAN_EXECUTE_BLOCKED: + return SystemScriptFileEnforcement.Block; + + case WLDP_EXECUTION_POLICY.WLDP_CAN_EXECUTE_REQUIRE_SANDBOX: + return SystemScriptFileEnforcement.AllowConstrained; + + default: + // Fall through to legacy system policy checks. + System.Diagnostics.Debug.Assert(false, $"Unknown execution policy returned from WldCanExecute: {canExecuteResult}"); + break; + } + } + + // If HResult is unsuccessful (such as E_NOTIMPL (0x80004001)), fall through to legacy system checks. + } + catch (DllNotFoundException) + { + // Fall back to legacy system policy checks. + s_wldpCanExecuteAvailable = false; + } + catch (EntryPointNotFoundException) + { + // Fall back to legacy system policy checks. + s_wldpCanExecuteAvailable = false; + } + } + + // Original (legacy) WDAC and AppLocker system checks. + if (SystemPolicy.GetSystemLockdownPolicy() != SystemEnforcementMode.None) + { + switch (SystemPolicy.GetLockdownPolicy(filePath, fileHandle)) + { + case SystemEnforcementMode.Enforce: + return SystemScriptFileEnforcement.AllowConstrained; + + case SystemEnforcementMode.None: + case SystemEnforcementMode.Audit: + return SystemScriptFileEnforcement.Allow; + + default: + System.Diagnostics.Debug.Assert(false, "GetFilePolicyEnforcement: Unknown SystemEnforcementMode."); + return SystemScriptFileEnforcement.Block; + } + } + + return SystemScriptFileEnforcement.None; + } /// /// Gets lockdown policy as applied to a file. @@ -391,6 +498,14 @@ private static SystemEnforcementMode GetDebugLockdownPolicy(string path) /// True if the COM object is allowed, False otherwise. internal static bool IsClassInApprovedList(Guid clsid) { + // This method is called only if there is an AppLocker and/or WLDP system wide lock down enforcement policy. + if (s_cachedWldpSystemPolicy.GetValueOrDefault(SystemEnforcementMode.None) != SystemEnforcementMode.Enforce) + { + // No WLDP policy implies only AppLocker policy enforcement. Disallow all COM object instantiation. + return false; + } + + // WLDP policy must be in system wide enforcement, look up COM Id in WLDP approval list. try { WLDP_HOST_INFORMATION hostInformation = new WLDP_HOST_INFORMATION(); @@ -538,18 +653,67 @@ internal struct WLDP_HOST_INFORMATION internal IntPtr hSource; } + /// + /// Options for WldpCanExecuteFile method. + /// + [Flags] + internal enum WLDP_EXECUTION_EVALUATION_OPTIONS + { + WLDP_EXECUTION_EVALUATION_OPTION_NONE = 0x0, + WLDP_EXECUTION_EVALUATION_OPTION_EXECUTE_IN_INTERACTIVE_SESSION = 0x1 + } + + /// + /// Results from WldpCanExecuteFile method. + /// + internal enum WLDP_EXECUTION_POLICY + { + WLDP_CAN_EXECUTE_BLOCKED = 0, + WLDP_CAN_EXECUTE_ALLOWED = 1, + WLDP_CAN_EXECUTE_REQUIRE_SANDBOX = 2 + } + + /// + /// Powershell Script Host. + /// + internal static readonly Guid PowerShellHost = new Guid("8E9AAA7C-198B-4879-AE41-A50D47AD6458"); + /// /// Native methods for dealing with the lockdown policy. /// internal static class WldpNativeMethods { + /// + /// Returns a WLDP_EXECUTION_POLICY enum value indicating if and how a script file + /// should be executed. + /// + /// Host guid. + /// Evaluation options. + /// Evaluated file handle. + /// Auditing information string. + /// Evaluation result. + /// HResult value. + [DefaultDllImportSearchPathsAttribute(DllImportSearchPath.System32)] + [DllImportAttribute("wldp.dll", EntryPoint = "WldpCanExecuteFile")] + internal static extern int WldpCanExecuteFile( + [MarshalAs(UnmanagedType.LPStruct)] + Guid host, + WLDP_EXECUTION_EVALUATION_OPTIONS options, + IntPtr fileHandle, + [MarshalAs(UnmanagedType.LPWStr)] + string auditInfo, + out WLDP_EXECUTION_POLICY result); + /// Return Type: HRESULT->LONG->int /// pHostInformation: PWLDP_HOST_INFORMATION->_WLDP_HOST_INFORMATION* /// pdwLockdownState: PDWORD->DWORD* /// dwFlags: DWORD->unsigned int [DefaultDllImportSearchPathsAttribute(DllImportSearchPath.System32)] [DllImportAttribute("wldp.dll", EntryPoint = "WldpGetLockdownPolicy")] - internal static extern int WldpGetLockdownPolicy(ref WLDP_HOST_INFORMATION pHostInformation, ref uint pdwLockdownState, uint dwFlags); + internal static extern int WldpGetLockdownPolicy( + ref WLDP_HOST_INFORMATION pHostInformation, + ref uint pdwLockdownState, + uint dwFlags); /// Return Type: HRESULT->LONG->int /// rclsid: IID* @@ -558,7 +722,11 @@ internal static class WldpNativeMethods /// dwFlags: DWORD->unsigned int [DefaultDllImportSearchPathsAttribute(DllImportSearchPath.System32)] [DllImportAttribute("wldp.dll", EntryPoint = "WldpIsClassInApprovedList")] - internal static extern int WldpIsClassInApprovedList(ref Guid rclsid, ref WLDP_HOST_INFORMATION pHostInformation, ref int ptIsApproved, uint dwFlags); + internal static extern int WldpIsClassInApprovedList( + ref Guid rclsid, + ref WLDP_HOST_INFORMATION pHostInformation, + ref int ptIsApproved, + uint dwFlags); [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern int SHGetKnownFolderPath( diff --git a/src/System.Management.Automation/singleshell/config/MshSnapinInfo.cs b/src/System.Management.Automation/singleshell/config/MshSnapinInfo.cs index b7e5d60f001..05f8991e4a9 100644 --- a/src/System.Management.Automation/singleshell/config/MshSnapinInfo.cs +++ b/src/System.Management.Automation/singleshell/config/MshSnapinInfo.cs @@ -118,25 +118,13 @@ string vendorFallback version = new Version("0.0"); } - if (types == null) - { - types = new Collection(); - } + types ??= new Collection(); - if (formats == null) - { - formats = new Collection(); - } + formats ??= new Collection(); - if (descriptionFallback == null) - { - descriptionFallback = string.Empty; - } + descriptionFallback ??= string.Empty; - if (vendorFallback == null) - { - vendorFallback = string.Empty; - } + vendorFallback ??= string.Empty; Name = name; IsDefault = isDefault; @@ -877,7 +865,7 @@ internal static Version ReadVersionValue(RegistryKey mshsnapinKey, string name, return v; } - internal static void ReadRegistryInfo(out Version assemblyVersion, out string publicKeyToken, out string culture, out string architecture, out string applicationBase, out Version psVersion) + internal static void ReadRegistryInfo(out Version assemblyVersion, out string publicKeyToken, out string culture, out string applicationBase, out Version psVersion) { applicationBase = Utils.DefaultPowerShellAppBase; Dbg.Assert( @@ -897,8 +885,7 @@ internal static void ReadRegistryInfo(out Version assemblyVersion, out string pu // culture, publickeytoken...This will break the scenarios where only one of // the assemblies is patched. ie., all monad assemblies should have the // same version number. - Assembly currentAssembly = typeof(PSSnapInReader).Assembly; - AssemblyName assemblyName = currentAssembly.GetName(); + AssemblyName assemblyName = typeof(PSSnapInReader).Assembly.GetName(); assemblyVersion = assemblyName.Version; byte[] publicTokens = assemblyName.GetPublicKeyToken(); if (publicTokens.Length == 0) @@ -911,11 +898,6 @@ internal static void ReadRegistryInfo(out Version assemblyVersion, out string pu // save some cpu cycles by hardcoding the culture to neutral // assembly should never be targeted to a particular culture culture = "neutral"; - - // Hardcoding the architecture MSIL as PowerShell assemblies are architecture neutral, this should - // be changed if the assumption is broken. Preferred hardcoded string to using (for perf reasons): - // string architecture = currentAssembly.GetName().ProcessorArchitecture.ToString() - architecture = "MSIL"; } /// @@ -943,13 +925,12 @@ internal static string ConvertByteArrayToString(byte[] tokens) /// internal static PSSnapInInfo ReadCoreEngineSnapIn() { - Version assemblyVersion, psVersion; - string publicKeyToken = null; - string culture = null; - string architecture = null; - string applicationBase = null; - - ReadRegistryInfo(out assemblyVersion, out publicKeyToken, out culture, out architecture, out applicationBase, out psVersion); + ReadRegistryInfo( + out Version assemblyVersion, + out string publicKeyToken, + out string culture, + out string applicationBase, + out Version psVersion); // System.Management.Automation formats & types files Collection types = new Collection(new string[] { "types.ps1xml", "typesv3.ps1xml" }); @@ -958,8 +939,8 @@ internal static PSSnapInInfo ReadCoreEngineSnapIn() "Help.format.ps1xml", "HelpV3.format.ps1xml", "PowerShellCore.format.ps1xml", "PowerShellTrace.format.ps1xml", "Registry.format.ps1xml"}); - string strongName = string.Format(CultureInfo.InvariantCulture, "{0}, Version={1}, Culture={2}, PublicKeyToken={3}, ProcessorArchitecture={4}", - s_coreSnapin.AssemblyName, assemblyVersion, culture, publicKeyToken, architecture); + string strongName = string.Format(CultureInfo.InvariantCulture, "{0}, Version={1}, Culture={2}, PublicKeyToken={3}", + s_coreSnapin.AssemblyName, assemblyVersion, culture, publicKeyToken); string moduleName = Path.Combine(applicationBase, s_coreSnapin.AssemblyName + ".dll"); @@ -997,13 +978,12 @@ internal static PSSnapInInfo ReadCoreEngineSnapIn() /// internal static Collection ReadEnginePSSnapIns() { - Version assemblyVersion, psVersion; - string publicKeyToken = null; - string culture = null; - string architecture = null; - string applicationBase = null; - - ReadRegistryInfo(out assemblyVersion, out publicKeyToken, out culture, out architecture, out applicationBase, out psVersion); + ReadRegistryInfo( + out Version assemblyVersion, + out string publicKeyToken, + out string culture, + out string applicationBase, + out Version psVersion); // System.Management.Automation formats & types files Collection smaFormats = new Collection(new string[] @@ -1022,12 +1002,11 @@ internal static Collection ReadEnginePSSnapIns() string strongName = string.Format( CultureInfo.InvariantCulture, - "{0}, Version={1}, Culture={2}, PublicKeyToken={3}, ProcessorArchitecture={4}", + "{0}, Version={1}, Culture={2}, PublicKeyToken={3}", defaultMshSnapinInfo.AssemblyName, assemblyVersionString, culture, - publicKeyToken, - architecture); + publicKeyToken); Collection formats = null; Collection types = null; @@ -1296,7 +1275,9 @@ private static IList DefaultMshSnapins { lock (s_syncObject) { +#pragma warning disable IDE0074 // Disabling the rule because it can't be applied on non Unix if (s_defaultMshSnapins == null) +#pragma warning restore IDE0074 { s_defaultMshSnapins = new List() { diff --git a/src/System.Management.Automation/utils/ClrFacade.cs b/src/System.Management.Automation/utils/ClrFacade.cs index 0372b249ca1..cabac1bd335 100644 --- a/src/System.Management.Automation/utils/ClrFacade.cs +++ b/src/System.Management.Automation/utils/ClrFacade.cs @@ -356,7 +356,7 @@ internal static string ToDmtfDateTime(DateTime date) dmtfDateTime += date.Second.ToString(frmInt32).PadLeft(2, '0'); dmtfDateTime += "."; - // Construct a DateTime with with the precision to Second as same as the passed DateTime and so get + // Construct a DateTime with the precision to Second as same as the passed DateTime and so get // the ticks difference so that the microseconds can be calculated DateTime dtTemp = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, 0); Int64 microsec = ((date.Ticks - dtTemp.Ticks) * 1000) / TimeSpan.TicksPerMillisecond; diff --git a/src/System.Management.Automation/utils/CommandDiscoveryExceptions.cs b/src/System.Management.Automation/utils/CommandDiscoveryExceptions.cs index e08d8e1a65c..4ce3568cf5a 100644 --- a/src/System.Management.Automation/utils/CommandDiscoveryExceptions.cs +++ b/src/System.Management.Automation/utils/CommandDiscoveryExceptions.cs @@ -121,14 +121,11 @@ public override ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - _errorId, - _errorCategory, - _commandName); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + _errorId, + _errorCategory, + _commandName); return _errorRecord; } diff --git a/src/System.Management.Automation/utils/CryptoUtils.cs b/src/System.Management.Automation/utils/CryptoUtils.cs index a8f27732a7b..26d1fd7d177 100644 --- a/src/System.Management.Automation/utils/CryptoUtils.cs +++ b/src/System.Management.Automation/utils/CryptoUtils.cs @@ -616,15 +616,8 @@ private void Dispose(bool disposing) { if (disposing) { - if (_rsa != null) - { - _rsa.Dispose(); - } - - if (_aes != null) - { - _aes.Dispose(); - } + _rsa?.Dispose(); + _aes?.Dispose(); } } @@ -635,7 +628,7 @@ private void Dispose(bool disposing) /// Helper for exchanging keys and encrypting/decrypting /// secure strings for serialization in remoting. /// - internal abstract class PSRemotingCryptoHelper : IDisposable + public abstract class PSRemotingCryptoHelper : IDisposable { #region Protected Members @@ -645,7 +638,7 @@ internal abstract class PSRemotingCryptoHelper : IDisposable /// it and performing symmetric key operations using the /// session key. /// - protected PSRSACryptoServiceProvider _rsaCryptoProvider; + internal PSRSACryptoServiceProvider _rsaCryptoProvider; /// /// Key exchange has been completed and both keys @@ -851,11 +844,7 @@ public void Dispose(bool disposing) { if (disposing) { - if (_rsaCryptoProvider != null) - { - _rsaCryptoProvider.Dispose(); - } - + _rsaCryptoProvider?.Dispose(); _rsaCryptoProvider = null; _keyExchangeCompleted.Dispose(); diff --git a/src/System.Management.Automation/utils/ExecutionExceptions.cs b/src/System.Management.Automation/utils/ExecutionExceptions.cs index 226a9cb8cdd..ed04086101e 100644 --- a/src/System.Management.Automation/utils/ExecutionExceptions.cs +++ b/src/System.Management.Automation/utils/ExecutionExceptions.cs @@ -161,14 +161,11 @@ public override ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - "CmdletInvocationException", - ErrorCategory.NotSpecified, - null); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + "CmdletInvocationException", + ErrorCategory.NotSpecified, + null); return _errorRecord; } @@ -891,14 +888,11 @@ public ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - "CallDepthOverflow", - ErrorCategory.InvalidOperation, - CallDepth); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + "CallDepthOverflow", + ErrorCategory.InvalidOperation, + CallDepth); return _errorRecord; } @@ -999,14 +993,11 @@ public ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - "CallDepthOverflow", - ErrorCategory.InvalidOperation, - CallDepth); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + "CallDepthOverflow", + ErrorCategory.InvalidOperation, + CallDepth); return _errorRecord; } diff --git a/src/System.Management.Automation/utils/MshArgumentException.cs b/src/System.Management.Automation/utils/MshArgumentException.cs index d9c9ce725eb..8bc58e6c429 100644 --- a/src/System.Management.Automation/utils/MshArgumentException.cs +++ b/src/System.Management.Automation/utils/MshArgumentException.cs @@ -121,14 +121,11 @@ public ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - _errorId, - ErrorCategory.InvalidArgument, - null); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + _errorId, + ErrorCategory.InvalidArgument, + null); return _errorRecord; } diff --git a/src/System.Management.Automation/utils/MshArgumentNullException.cs b/src/System.Management.Automation/utils/MshArgumentNullException.cs index be20163607b..294c91ea438 100644 --- a/src/System.Management.Automation/utils/MshArgumentNullException.cs +++ b/src/System.Management.Automation/utils/MshArgumentNullException.cs @@ -119,14 +119,11 @@ public ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - _errorId, - ErrorCategory.InvalidArgument, - null); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + _errorId, + ErrorCategory.InvalidArgument, + null); return _errorRecord; } diff --git a/src/System.Management.Automation/utils/MshArgumentOutOfRangeException.cs b/src/System.Management.Automation/utils/MshArgumentOutOfRangeException.cs index 2bf48e9047f..b8db294e99c 100644 --- a/src/System.Management.Automation/utils/MshArgumentOutOfRangeException.cs +++ b/src/System.Management.Automation/utils/MshArgumentOutOfRangeException.cs @@ -117,14 +117,11 @@ public ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - _errorId, - ErrorCategory.InvalidArgument, - null); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + _errorId, + ErrorCategory.InvalidArgument, + null); return _errorRecord; } diff --git a/src/System.Management.Automation/utils/MshInvalidOperationException.cs b/src/System.Management.Automation/utils/MshInvalidOperationException.cs index f9a65fcf8aa..ff238ef20ca 100644 --- a/src/System.Management.Automation/utils/MshInvalidOperationException.cs +++ b/src/System.Management.Automation/utils/MshInvalidOperationException.cs @@ -115,14 +115,11 @@ public ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - _errorId, - _errorCategory, - _target); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + _errorId, + _errorCategory, + _target); return _errorRecord; } diff --git a/src/System.Management.Automation/utils/MshNotImplementedException.cs b/src/System.Management.Automation/utils/MshNotImplementedException.cs index 6c2dd504cf0..e6e1d1b54eb 100644 --- a/src/System.Management.Automation/utils/MshNotImplementedException.cs +++ b/src/System.Management.Automation/utils/MshNotImplementedException.cs @@ -98,14 +98,11 @@ public ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - _errorId, - ErrorCategory.NotImplemented, - null); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + _errorId, + ErrorCategory.NotImplemented, + null); return _errorRecord; } diff --git a/src/System.Management.Automation/utils/MshNotSupportedException.cs b/src/System.Management.Automation/utils/MshNotSupportedException.cs index 7614080dcff..d7002f9111e 100644 --- a/src/System.Management.Automation/utils/MshNotSupportedException.cs +++ b/src/System.Management.Automation/utils/MshNotSupportedException.cs @@ -98,14 +98,11 @@ public ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - _errorId, - ErrorCategory.NotImplemented, - null); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + _errorId, + ErrorCategory.NotImplemented, + null); return _errorRecord; } diff --git a/src/System.Management.Automation/utils/MshObjectDisposedException.cs b/src/System.Management.Automation/utils/MshObjectDisposedException.cs index b8d4ab421cc..8ceabb84227 100644 --- a/src/System.Management.Automation/utils/MshObjectDisposedException.cs +++ b/src/System.Management.Automation/utils/MshObjectDisposedException.cs @@ -104,14 +104,11 @@ public ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - _errorId, - ErrorCategory.InvalidOperation, - null); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + _errorId, + ErrorCategory.InvalidOperation, + null); return _errorRecord; } diff --git a/src/System.Management.Automation/utils/ObjectStream.cs b/src/System.Management.Automation/utils/ObjectStream.cs index 2f4cbef8a08..8bf4adc03b4 100644 --- a/src/System.Management.Automation/utils/ObjectStream.cs +++ b/src/System.Management.Automation/utils/ObjectStream.cs @@ -609,15 +609,12 @@ internal override WaitHandle ReadHandle lock (_monitorObject) { - if (_readWaitHandle == null) - { - // Create the handle signaled if there are objects in the stream - // or the stream has been closed. The closed scenario addresses - // Pipeline readers that execute asynchronously. Since the pipeline - // may complete with zero objects before the caller objects this - // handle, it will block indefinitely unless it is set. - _readWaitHandle = new ManualResetEvent(_objects.Count > 0 || !_isOpen); - } + // Create the handle signaled if there are objects in the stream + // or the stream has been closed. The closed scenario addresses + // Pipeline readers that execute asynchronously. Since the pipeline + // may complete with zero objects before the caller objects this + // handle, it will block indefinitely unless it is set. + _readWaitHandle ??= new ManualResetEvent(_objects.Count > 0 || !_isOpen); handle = _readWaitHandle; } @@ -642,10 +639,7 @@ internal override WaitHandle WriteHandle lock (_monitorObject) { - if (_writeWaitHandle == null) - { - _writeWaitHandle = new ManualResetEvent(_objects.Count < _capacity || !_isOpen); - } + _writeWaitHandle ??= new ManualResetEvent(_objects.Count < _capacity || !_isOpen); handle = _writeWaitHandle; } @@ -665,17 +659,14 @@ internal override PipelineReader ObjectReader lock (_monitorObject) { - if (_reader == null) - { - // Always return an object reader, even if the stream - // is closed. This is to address requesting the object reader - // after calling Pipeline.Execute(). NOTE: If Execute completes - // without writing data to the output queue, the - // stream will be in the EndOfPipeline state because the - // stream is closed and has zero data. Since this is a valid - // and expected execution path, we don't want to throw an exception. - _reader = new ObjectReader(this); - } + // Always return an object reader, even if the stream + // is closed. This is to address requesting the object reader + // after calling Pipeline.Execute(). NOTE: If Execute completes + // without writing data to the output queue, the + // stream will be in the EndOfPipeline state because the + // stream is closed and has zero data. Since this is a valid + // and expected execution path, we don't want to throw an exception. + _reader ??= new ObjectReader(this); reader = _reader; } @@ -695,17 +686,14 @@ internal override PipelineReader PSObjectReader lock (_monitorObject) { - if (_mshreader == null) - { - // Always return an object reader, even if the stream - // is closed. This is to address requesting the object reader - // after calling Pipeline.Execute(). NOTE: If Execute completes - // without writing data to the output queue, the - // stream will be in the EndOfPipeline state because the - // stream is closed and has zero data. Since this is a valid - // and expected execution path, we don't want to throw an exception. - _mshreader = new PSObjectReader(this); - } + // Always return an object reader, even if the stream + // is closed. This is to address requesting the object reader + // after calling Pipeline.Execute(). NOTE: If Execute completes + // without writing data to the output queue, the + // stream will be in the EndOfPipeline state because the + // stream is closed and has zero data. Since this is a valid + // and expected execution path, we don't want to throw an exception. + _mshreader ??= new PSObjectReader(this); reader = _mshreader; } @@ -726,10 +714,7 @@ internal override PipelineWriter ObjectWriter lock (_monitorObject) { - if (_writer == null) - { - _writer = new ObjectWriter(this) as PipelineWriter; - } + _writer ??= new ObjectWriter(this) as PipelineWriter; writer = _writer; } @@ -1511,16 +1496,8 @@ protected override void Dispose(bool disposing) _writeHandle.Dispose(); _writeClosedHandle.Dispose(); _readClosedHandle.Dispose(); - - if (_readWaitHandle != null) - { - _readWaitHandle.Dispose(); - } - - if (_writeWaitHandle != null) - { - _writeWaitHandle.Dispose(); - } + _readWaitHandle?.Dispose(); + _writeWaitHandle?.Dispose(); if (_reader != null) { @@ -1686,10 +1663,7 @@ internal override PipelineReader ObjectReader { lock (_syncObject) { - if (_objectReader == null) - { - _objectReader = new PSDataCollectionReader(this); - } + _objectReader ??= new PSDataCollectionReader(this); } } @@ -1711,11 +1685,8 @@ internal PipelineReader GetObjectReaderForPipeline(string computerName, { lock (_syncObject) { - if (_objectReaderForPipeline == null) - { - _objectReaderForPipeline = - new PSDataCollectionPipelineReader(this, computerName, runspaceId); - } + _objectReaderForPipeline ??= + new PSDataCollectionPipelineReader(this, computerName, runspaceId); } } @@ -1733,10 +1704,7 @@ internal override PipelineReader PSObjectReader { lock (_syncObject) { - if (_psobjectReader == null) - { - _psobjectReader = new PSDataCollectionReader(this); - } + _psobjectReader ??= new PSDataCollectionReader(this); } } @@ -1758,11 +1726,8 @@ internal PipelineReader GetPSObjectReaderForPipeline(string computerNa { lock (_syncObject) { - if (_psobjectReaderForPipeline == null) - { - _psobjectReaderForPipeline = - new PSDataCollectionPipelineReader(this, computerName, runspaceId); - } + _psobjectReaderForPipeline ??= + new PSDataCollectionPipelineReader(this, computerName, runspaceId); } } @@ -1784,10 +1749,7 @@ internal override PipelineWriter ObjectWriter { lock (_syncObject) { - if (_writer == null) - { - _writer = new PSDataCollectionWriter(this) as PipelineWriter; - } + _writer ??= new PSDataCollectionWriter(this) as PipelineWriter; } } diff --git a/src/System.Management.Automation/utils/PInvokeDllNames.cs b/src/System.Management.Automation/utils/PInvokeDllNames.cs index bff2ca2656e..a1bbe282aa7 100644 --- a/src/System.Management.Automation/utils/PInvokeDllNames.cs +++ b/src/System.Management.Automation/utils/PInvokeDllNames.cs @@ -89,7 +89,7 @@ internal static class PinvokeDllNames internal const string PrivilegeCheckDllName = "api-ms-win-security-base-l1-1-0.dll"; /*76*/ internal const string ImpersonateNamedPipeClientDllName = "api-ms-win-core-namedpipe-l1-1-0.dll"; /*77*/ internal const string RevertToSelfDllName = "api-ms-win-security-base-l1-1-0.dll"; /*78*/ - internal const string CreateProcessInComputeSystemDllName = "vmcompute.dll"; /*79*/ + internal const string CreateProcessInComputeSystemDllName = "ComputeCore.dll"; /*79*/ internal const string CLSIDFromProgIDDllName = "api-ms-win-core-com-l1-1-0.dll"; /*80*/ internal const string LoadLibraryEx = "api-ms-win-core-libraryloader-l1-1-0.dll"; /*81*/ internal const string FreeLibrary = "api-ms-win-core-libraryloader-l1-1-0.dll"; /*82*/ diff --git a/src/System.Management.Automation/utils/ParameterBinderExceptions.cs b/src/System.Management.Automation/utils/ParameterBinderExceptions.cs index 841523f6bdb..fcb10737d35 100644 --- a/src/System.Management.Automation/utils/ParameterBinderExceptions.cs +++ b/src/System.Management.Automation/utils/ParameterBinderExceptions.cs @@ -212,10 +212,7 @@ internal ParameterBindingException( _parameterType = parameterType; _typeSpecified = typeSpecified; - if (errorPosition == null) - { - errorPosition = invocationInfo.ScriptPosition; - } + errorPosition ??= invocationInfo.ScriptPosition; if (errorPosition != null) { diff --git a/src/System.Management.Automation/utils/PathUtils.cs b/src/System.Management.Automation/utils/PathUtils.cs index 0034681e126..48d0acb5f49 100644 --- a/src/System.Management.Automation/utils/PathUtils.cs +++ b/src/System.Management.Automation/utils/PathUtils.cs @@ -7,7 +7,7 @@ using System.Management.Automation.Internal; using System.Runtime.CompilerServices; using System.Text; - +using Microsoft.PowerShell.Commands; using Dbg = System.Management.Automation.Diagnostics; namespace System.Management.Automation @@ -357,7 +357,9 @@ internal static DirectoryInfo CreateModuleDirectory(PSCmdlet cmdlet, string modu DirectoryInfo directoryInfo = null; try { - string rootedPath = Microsoft.PowerShell.Commands.ModuleCmdletBase.ResolveRootedFilePath(moduleNameOrPath, cmdlet.Context); + // Even if 'moduleNameOrPath' is a rooted path, 'ResolveRootedFilePath' may return null when the path doesn't exist yet, + // or when it contains wildcards but cannot be resolved to a single path. + string rootedPath = ModuleCmdletBase.ResolveRootedFilePath(moduleNameOrPath, cmdlet.Context); if (string.IsNullOrEmpty(rootedPath) && moduleNameOrPath.StartsWith('.')) { PathInfo currentPath = cmdlet.CurrentProviderLocation(cmdlet.Context.ProviderNames.FileSystem); @@ -366,8 +368,25 @@ internal static DirectoryInfo CreateModuleDirectory(PSCmdlet cmdlet, string modu if (string.IsNullOrEmpty(rootedPath)) { - string personalModuleRoot = ModuleIntrinsics.GetPersonalModulePath(); - rootedPath = Path.Combine(personalModuleRoot, moduleNameOrPath); + if (Path.IsPathRooted(moduleNameOrPath)) + { + rootedPath = moduleNameOrPath; + } + else + { + string personalModuleRoot = ModuleIntrinsics.GetPersonalModulePath(); + if (string.IsNullOrEmpty(personalModuleRoot)) + { + cmdlet.ThrowTerminatingError( + new ErrorRecord( + new ArgumentException(StringUtil.Format(PathUtilsStrings.ExportPSSession_ErrorModuleNameOrPath, moduleNameOrPath)), + "ExportPSSession_ErrorModuleNameOrPath", + ErrorCategory.InvalidArgument, + cmdlet)); + } + + rootedPath = Path.Combine(personalModuleRoot, moduleNameOrPath); + } } directoryInfo = new DirectoryInfo(rootedPath); diff --git a/src/System.Management.Automation/utils/PowerShellExecutionHelper.cs b/src/System.Management.Automation/utils/PowerShellExecutionHelper.cs index dcfc2682bcd..d30d759647a 100644 --- a/src/System.Management.Automation/utils/PowerShellExecutionHelper.cs +++ b/src/System.Management.Automation/utils/PowerShellExecutionHelper.cs @@ -46,12 +46,6 @@ internal PowerShellExecutionHelper(PowerShell powershell) #region Command Execution - internal Collection ExecuteCommand(string command) - { - Exception unused; - return ExecuteCommand(command, true, out unused, null); - } - internal bool ExecuteCommandAndGetResultAsBool() { Exception exceptionThrown; @@ -66,71 +60,6 @@ internal bool ExecuteCommandAndGetResultAsBool() return (streamResults.Count > 1) || (LanguagePrimitives.IsTrue(streamResults[0])); } - internal string ExecuteCommandAndGetResultAsString() - { - Exception exceptionThrown; - Collection streamResults = ExecuteCurrentPowerShell(out exceptionThrown); - - if (exceptionThrown != null || streamResults == null || streamResults.Count == 0) - { - return null; - } - - // we got back one or more objects. Pick off the first result. - if (streamResults[0] == null) - return string.Empty; - - // And convert the base object into a string. We can't use the proxied - // ToString() on the PSObject because there is no default runspace - // available. - return SafeToString(streamResults[0]); - } - - internal Collection ExecuteCommand(string command, bool isScript, out Exception exceptionThrown, Hashtable args) - { - Diagnostics.Assert(command != null, "caller to verify command is not null"); - - exceptionThrown = null; - - // This flag indicates a previous call to this method had its pipeline cancelled - if (CancelTabCompletion) - { - return new Collection(); - } - - CurrentPowerShell.AddCommand(command); - - Command cmd = new Command(command, isScript); - if (args != null) - { - foreach (DictionaryEntry arg in args) - { - cmd.Parameters.Add((string)(arg.Key), arg.Value); - } - } - - Collection results = null; - try - { - // blocks until all results are retrieved. - // results = this.ExecuteCommand(cmd); - - // If this pipeline has been stopped lets set a flag to cancel all future tab completion calls - // untill the next completion - if (IsStopped) - { - results = new Collection(); - CancelTabCompletion = true; - } - } - catch (Exception e) - { - exceptionThrown = e; - } - - return results; - } - internal Collection ExecuteCurrentPowerShell(out Exception exceptionThrown, IEnumerable input = null) { exceptionThrown = null; @@ -147,7 +76,7 @@ internal Collection ExecuteCurrentPowerShell(out Exception exceptionTh results = CurrentPowerShell.Invoke(input); // If this pipeline has been stopped lets set a flag to cancel all future tab completion calls - // untill the next completion + // until the next completion if (IsStopped) { results = new Collection(); diff --git a/src/System.Management.Automation/utils/PsUtils.cs b/src/System.Management.Automation/utils/PsUtils.cs index 574f42eace3..667048d7709 100644 --- a/src/System.Management.Automation/utils/PsUtils.cs +++ b/src/System.Management.Automation/utils/PsUtils.cs @@ -83,31 +83,6 @@ internal static bool IsRunningOnProcessorArchitectureARM() return arch == Architecture.Arm || arch == Architecture.Arm64; } - /// - /// Get a temporary directory to use, needs to be unique to avoid collision. - /// - internal static string GetTemporaryDirectory() - { - string tempDir = string.Empty; - string tempPath = Path.GetTempPath(); - do - { - tempDir = Path.Combine(tempPath, System.Guid.NewGuid().ToString()); - } - while (Directory.Exists(tempDir)); - - try - { - Directory.CreateDirectory(tempDir); - } - catch (UnauthorizedAccessException) - { - tempDir = string.Empty; // will become current working directory - } - - return tempDir; - } - internal static string GetHostName() { IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); @@ -375,9 +350,7 @@ internal static Hashtable GetModuleManifestProperties(string psDataFilePath, str pe.Message); } - string unused1; - string unused2; - var pipeline = ast.GetSimplePipeline(false, out unused1, out unused2); + var pipeline = ast.GetSimplePipeline(false, out _, out _); if (pipeline != null) { var hashtableAst = pipeline.GetPureExpression() as HashtableAst; diff --git a/src/System.Management.Automation/utils/RuntimeException.cs b/src/System.Management.Automation/utils/RuntimeException.cs index 4ba776799e7..d3e204f450c 100644 --- a/src/System.Management.Automation/utils/RuntimeException.cs +++ b/src/System.Management.Automation/utils/RuntimeException.cs @@ -151,14 +151,11 @@ public virtual ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - _errorId, - _errorCategory, - _targetObject); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + _errorId, + _errorCategory, + _targetObject); return _errorRecord; } @@ -214,8 +211,7 @@ internal void SetErrorCategory(ErrorCategory errorCategory) internal void SetTargetObject(object targetObject) { _targetObject = targetObject; - if (_errorRecord != null) - _errorRecord.SetTargetObject(targetObject); + _errorRecord?.SetTargetObject(targetObject); } #endregion ErrorRecord diff --git a/src/System.Management.Automation/utils/SessionStateExceptions.cs b/src/System.Management.Automation/utils/SessionStateExceptions.cs index a08e7615dc5..34ad5c7f4e0 100644 --- a/src/System.Management.Automation/utils/SessionStateExceptions.cs +++ b/src/System.Management.Automation/utils/SessionStateExceptions.cs @@ -234,14 +234,11 @@ public override ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - "ProviderInvocationException", - ErrorCategory.NotSpecified, - null); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + "ProviderInvocationException", + ErrorCategory.NotSpecified, + null); return _errorRecord; } @@ -497,14 +494,11 @@ public override ErrorRecord ErrorRecord { get { - if (_errorRecord == null) - { - _errorRecord = new ErrorRecord( - new ParentContainsErrorRecordException(this), - _errorId, - _errorCategory, - _itemName); - } + _errorRecord ??= new ErrorRecord( + new ParentContainsErrorRecordException(this), + _errorId, + _errorCategory, + _itemName); return _errorRecord; } diff --git a/src/System.Management.Automation/utils/StringUtil.cs b/src/System.Management.Automation/utils/StringUtil.cs index c84b3f5e9fe..6bdedd03a3b 100644 --- a/src/System.Management.Automation/utils/StringUtil.cs +++ b/src/System.Management.Automation/utils/StringUtil.cs @@ -1,8 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Collections.Generic; using System.Globalization; using System.Management.Automation.Host; +using System.Text; +using System.Text.RegularExpressions; using System.Threading; using Dbg = System.Management.Automation.Diagnostics; @@ -93,5 +96,178 @@ internal static string DashPadding(int count) return result; } + + /// + /// Substring implementation that takes into account the VT escape sequences. + /// + /// String that may contain VT escape sequences. + /// + /// When the string doesn't contain VT sequences, it's the starting index. + /// When the string contains VT sequences, it means starting from the 'n-th' char that doesn't belong to a escape sequence. + /// + /// The requested substring. + internal static string VtSubstring(this string str, int startOffset) + { + return VtSubstring(str, startOffset, int.MaxValue, prependStr: null, appendStr: null); + } + + /// + /// Substring implementation that takes into account the VT escape sequences. + /// + /// String that may contain VT escape sequences. + /// + /// When the string doesn't contain VT sequences, it's the starting index. + /// When the string contains VT sequences, it means starting from the 'n-th' char that doesn't belong to a escape sequence. + /// Number of non-escape-sequence characters to be included in the substring. + /// The requested substring. + internal static string VtSubstring(this string str, int startOffset, int length) + { + return VtSubstring(str, startOffset, length, prependStr: null, appendStr: null); + } + + /// + /// Substring implementation that takes into account the VT escape sequences. + /// + /// String that may contain VT escape sequences. + /// + /// When the string doesn't contain VT sequences, it's the starting index. + /// When the string contains VT sequences, it means starting from the 'n-th' char that doesn't belong to a escape sequence. + /// The string to be prepended to the substring. + /// The string to be appended to the substring. + /// The requested substring. + internal static string VtSubstring(this string str, int startOffset, string prependStr, string appendStr) + { + return VtSubstring(str, startOffset, int.MaxValue, prependStr, appendStr); + } + + /// + /// Substring implementation that takes into account the VT escape sequences. + /// + /// String that may contain VT escape sequences. + /// + /// When the string doesn't contain VT sequences, it's the starting index. + /// When the string contains VT sequences, it means starting from the 'n-th' char that doesn't belong to a escape sequence. + /// Number of non-escape-sequence characters to be included in the substring. + /// The string to be prepended to the substring. + /// The string to be appended to the substring. + /// The requested substring. + internal static string VtSubstring(this string str, int startOffset, int length, string prependStr, string appendStr) + { + var valueStrDec = new ValueStringDecorated(str); + if (valueStrDec.IsDecorated) + { + // Handle strings with VT sequences. + bool copyStarted = startOffset == 0; + bool hasEscSeqs = false; + bool firstNonEscChar = true; + StringBuilder sb = new(capacity: str.Length); + Dictionary vtRanges = valueStrDec.EscapeSequenceRanges; + + for (int i = 0, offset = 0; i < str.Length; i++) + { + // Keep all leading ANSI escape sequences. + if (vtRanges.TryGetValue(i, out int len)) + { + hasEscSeqs = true; + sb.Append(str.AsSpan(i, len)); + + i += len - 1; + continue; + } + + // OK, now we get a non-escape-sequence character. + if (copyStarted) + { + if (firstNonEscChar) + { + // Prepend the string before we copy the first non-escape-sequence character. + sb.Append(prependStr); + firstNonEscChar = false; + } + + // Copy this character if we've started the copy. + sb.Append(str[i]); + + // Increment 'offset' to keep track of number of non-escape-sequence characters we've copied. + offset++; + } + else if (++offset == startOffset) + { + // We've skipped enough non-escape-sequence characters, and will be copying the next one. + copyStarted = true; + + // Reset 'offset' and from now on use it to track the number of copied non-escape-sequence characters. + offset = 0; + continue; + } + + // If the number of copied non-escape-sequence characters has reached the specified length, done copying. + if (offset == length) + { + break; + } + } + + if (hasEscSeqs) + { + string resetStr = PSStyle.Instance.Reset; + bool endsWithReset = sb.EndsWith(resetStr); + if (endsWithReset) + { + // Append the given string before the reset VT sequence. + sb.Insert(sb.Length - resetStr.Length, appendStr); + } + else + { + // Append the given string and add the reset VT sequence. + sb.Append(appendStr).Append(resetStr); + } + } + else + { + sb.Append(appendStr); + } + + return sb.ToString(); + } + + // Handle strings without VT sequences. + if (length == int.MaxValue) + { + length = str.Length - startOffset; + } + + if (prependStr is null && appendStr is null) + { + return str.Substring(startOffset, length); + } + else + { + int capacity = length + prependStr?.Length ?? 0 + appendStr?.Length ?? 0; + return new StringBuilder(prependStr, capacity) + .Append(str, startOffset, length) + .Append(appendStr) + .ToString(); + } + } + + internal static bool EndsWith(this StringBuilder sb, string value) + { + if (sb.Length < value.Length) + { + return false; + } + + int offset = sb.Length - value.Length; + for (int i = 0; i < value.Length; i++) + { + if (sb[offset + i] != value[i]) + { + return false; + } + } + + return true; + } } } diff --git a/src/System.Management.Automation/utils/Telemetry.cs b/src/System.Management.Automation/utils/Telemetry.cs index 0ebdfc06e12..3d9f3c9e1f7 100644 --- a/src/System.Management.Automation/utils/Telemetry.cs +++ b/src/System.Management.Automation/utils/Telemetry.cs @@ -3,13 +3,16 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Management.Automation; using System.Runtime.InteropServices; using System.Threading; using Microsoft.ApplicationInsights; +using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.Extensibility; +using Microsoft.ApplicationInsights.Extensibility.Implementation; namespace Microsoft.PowerShell.Telemetry { @@ -60,6 +63,26 @@ internal enum TelemetryType RemoteSessionOpen, } + /// + /// Set up the telemetry initializer to mask the platform specific names. + /// + internal class NameObscurerTelemetryInitializer : ITelemetryInitializer + { + // Report the platform name information as "na". + private const string _notavailable = "na"; + + /// + /// Initialize properties we are obscuring to "na". + /// + /// The instance of our telemetry. + public void Initialize(ITelemetry telemetry) + { + telemetry.Context.Cloud.RoleName = _notavailable; + telemetry.Context.GetInternalContext().NodeName = _notavailable; + telemetry.Context.Cloud.RoleInstance = _notavailable; + } + } + /// /// Send up telemetry for startup. /// @@ -72,8 +95,15 @@ public static class ApplicationInsightsTelemetry // private const string _psCoreTelemetryKey = "ee4b2115-d347-47b0-adb6-b19c2c763808"; // Production private const string _psCoreTelemetryKey = "d26a5ef4-d608-452c-a6b8-a4a55935f70d"; // V7 Preview 3 + // In the event there is a problem in creating the node identifier file, use the default identifier. + // This can happen if we are running in a system which has a read-only filesystem. + private static readonly Guid _defaultNodeIdentifier = new Guid("2f998828-3f4a-4741-bf50-d11c6be42f50"); + // Use "anonymous" as the string to return when you can't report a name - private const string _anonymous = "anonymous"; + private const string Anonymous = "anonymous"; + + // Use '0.0' as the string for an anonymous module version + private const string AnonymousVersion = "0.0"; // the telemetry failure string private const string _telemetryFailure = "TELEMETRY_FAILURE"; @@ -111,14 +141,17 @@ static ApplicationInsightsTelemetry() CanSendTelemetry = !GetEnvironmentVariableAsBool(name: _telemetryOptoutEnvVar, defaultValue: false); if (CanSendTelemetry) { + s_sessionId = Guid.NewGuid().ToString(); TelemetryConfiguration configuration = TelemetryConfiguration.CreateDefault(); - configuration.InstrumentationKey = _psCoreTelemetryKey; + configuration.ConnectionString = "InstrumentationKey=" + _psCoreTelemetryKey; // Set this to true to reduce latency during development configuration.TelemetryChannel.DeveloperMode = false; + // Be sure to obscure any information about the client node name. + configuration.TelemetryInitializers.Add(new NameObscurerTelemetryInitializer()); + s_telemetryClient = new TelemetryClient(configuration); - s_sessionId = Guid.NewGuid().ToString(); // use a hashset when looking for module names, it should be quicker than a string comparison s_knownModules = new HashSet(StringComparer.OrdinalIgnoreCase) @@ -215,6 +248,7 @@ static ApplicationInsightsTelemetry() "Az.StorageTable", "Az.StreamAnalytics", "Az.Subscription", + "Az.Tools.Predictor", "Az.TrafficManager", "Az.Websites", "Azs.Azurebridge.Admin", @@ -318,6 +352,7 @@ static ApplicationInsightsTelemetry() "branchcache", "CimCmdlets", "clusterawareupdating", + "CompatPowerShellGet", "configci", "ConfigurationManager", "DataProtectionManager", @@ -616,6 +651,28 @@ private static bool GetEnvironmentVariableAsBool(string name, bool defaultValue) return defaultValue; } + /// + /// Send module load telemetry as a metric. + /// For modules we send the module name (if allowed), and the version. + /// Some modules (CIM) will continue use the string alternative method. + /// + /// The type of telemetry that we'll be sending. + /// The module name to report. If it is not allowed, then it is set to 'anonymous'. + /// The module version to report. The default value is the anonymous version '0.0.0.0'. + internal static void SendModuleTelemetryMetric(TelemetryType telemetryType, string moduleName, string moduleVersion = AnonymousVersion) + { + try + { + string allowedModuleName = GetModuleName(moduleName); + string allowedModuleVersion = allowedModuleName == Anonymous ? AnonymousVersion : moduleVersion; + s_telemetryClient.GetMetric(telemetryType.ToString(), "uuid", "SessionId", "ModuleName", "Version").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, allowedModuleName, allowedModuleVersion); + } + catch + { + // Ignore errors. + } + } + /// /// Send telemetry as a metric. /// @@ -628,7 +685,9 @@ internal static void SendTelemetryMetric(TelemetryType metricId, string data) return; } - SendPSCoreStartupTelemetry("hosted"); + // These should be handled by SendModuleTelemetryMetric. + Debug.Assert(metricId != TelemetryType.ModuleLoad, "ModuleLoad should be handled by SendModuleTelemetryMetric."); + Debug.Assert(metricId != TelemetryType.WinCompatModuleLoad, "WinCompatModuleLoad should be handled by SendModuleTelemetryMetric."); string metricName = metricId.ToString(); try @@ -645,11 +704,6 @@ internal static void SendTelemetryMetric(TelemetryType metricId, string data) string experimentalFeatureName = GetExperimentalFeatureName(data); s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, experimentalFeatureName); break; - case TelemetryType.ModuleLoad: - case TelemetryType.WinCompatModuleLoad: - string moduleName = GetModuleName(data); // This will return anonymous if the modulename is not on the report list - s_telemetryClient.GetMetric(metricName, "uuid", "SessionId", "Detail").TrackValue(metricValue: 1.0, s_uniqueUserIdentifier, s_sessionId, moduleName); - break; } } catch @@ -671,7 +725,7 @@ private static string GetExperimentalFeatureName(string featureNameToValidate) return featureNameToValidate; } - return _anonymous; + return Anonymous; } // Get the module name. If we can report it, we'll return the name, otherwise, we'll return "anonymous" @@ -682,7 +736,7 @@ private static string GetModuleName(string moduleNameToValidate) return moduleNameToValidate; } - return _anonymous; + return Anonymous; } /// @@ -690,7 +744,8 @@ private static string GetModuleName(string moduleNameToValidate) /// This is done only once during for the console host. /// /// The "mode" of the startup. - internal static void SendPSCoreStartupTelemetry(string mode) + /// The parameter bitmap used when starting. + internal static void SendPSCoreStartupTelemetry(string mode, double parametersUsed) { // Check if we already sent startup telemetry if (Interlocked.CompareExchange(ref s_startupEventSent, 1, 0) == 1) @@ -703,22 +758,30 @@ internal static void SendPSCoreStartupTelemetry(string mode) return; } + // This is the payload which reports the startup information of OS and shell details. var properties = new Dictionary(); - // The variable POWERSHELL_DISTRIBUTION_CHANNEL is set in our docker images. - // This allows us to track the actual docker OS as OSDescription provides only "linuxkit" - // which has limited usefulness + // This is the payload for the parameter data which is sent as a metric. + var parameters = new Dictionary(); + + // The variable POWERSHELL_DISTRIBUTION_CHANNEL is set in our docker images and + // by various other environments. This allows us to track the actual docker OS as + // OSDescription provides only "linuxkit" which has limited usefulness. var channel = Environment.GetEnvironmentVariable("POWERSHELL_DISTRIBUTION_CHANNEL"); + // Construct the payload for the OS and shell details. properties.Add("SessionId", s_sessionId); properties.Add("UUID", s_uniqueUserIdentifier); properties.Add("GitCommitID", PSVersionInfo.GitCommitId); properties.Add("OSDescription", RuntimeInformation.OSDescription); properties.Add("OSChannel", string.IsNullOrEmpty(channel) ? "unknown" : channel); properties.Add("StartMode", string.IsNullOrEmpty(mode) ? "unknown" : mode); + + // Construct the payload for the parameters used. + parameters.Add("Param", parametersUsed); try { - s_telemetryClient.TrackEvent("ConsoleHostStartup", properties, null); + s_telemetryClient.TrackEvent("ConsoleHostStartup", properties, parameters); } catch { @@ -772,50 +835,53 @@ private static bool TryGetIdentifier(string telemetryFilePath, out Guid id) /// Try to create a unique identifier and persist it to the telemetry.uuid file. /// /// The path to the persisted telemetry.uuid file. - /// The created identifier. /// - /// The method returns a bool indicating success or failure of creating the id. + /// The method node id. /// - private static bool TryCreateUniqueIdentifierAndFile(string telemetryFilePath, out Guid id) + private static Guid CreateUniqueIdentifierAndFile(string telemetryFilePath) { // one last attempt to retrieve before creating incase we have a lot of simultaneous entry into the mutex. - id = Guid.Empty; + Guid id = Guid.Empty; if (TryGetIdentifier(telemetryFilePath, out id)) { - return true; + return id; } // The directory may not exist, so attempt to create it // CreateDirectory will simply return the directory if exists + bool attemptFileCreation = true; try { Directory.CreateDirectory(Path.GetDirectoryName(telemetryFilePath)); } catch { - // send a telemetry indicating a problem with the cache dir - // it's likely something is seriously wrong so we should at least report it. - // We don't want to provide reasons here, that's not the point, but we - // would like to know if we're having a generalized problem which we can trace statistically - CanSendTelemetry = false; - s_telemetryClient.GetMetric(_telemetryFailure, "Detail").TrackValue(1, "cachedir"); - return false; + // There was a problem in creating the directory for the file, do not attempt to create the file. + // We don't send telemetry here because there are valid reasons for the directory to not exist + // and not be able to be created. + attemptFileCreation = false; } - // Create and save the new identifier, and if there's a problem, disable telemetry - try + // If we were able to create the directory, try to create the file, + // if this fails we will send telemetry to indicate this and then use the default identifier. + if (attemptFileCreation) { - id = Guid.NewGuid(); - File.WriteAllBytes(telemetryFilePath, id.ToByteArray()); - return true; - } - catch - { - // another bit of telemetry to notify us about a problem with saving the unique id. - s_telemetryClient.GetMetric(_telemetryFailure, "Detail").TrackValue(1, "saveuuid"); + try + { + id = Guid.NewGuid(); + File.WriteAllBytes(telemetryFilePath, id.ToByteArray()); + return id; + } + catch + { + // another bit of telemetry to notify us about a problem with saving the unique id. + s_telemetryClient.GetMetric(_telemetryFailure, "Detail").TrackValue(1, "saveuuid"); + } } - return false; + // all attempts to create an identifier have failed, so use the default node id. + id = _defaultNodeIdentifier; + return id; } /// @@ -839,15 +905,12 @@ private static Guid GetUniqueIdentifier() // simultaneous shell starts without the persisted file which attempt to create the file. try { - // TryCreateUniqueIdentifierAndFile shouldn't throw, but the mutex might + // CreateUniqueIdentifierAndFile shouldn't throw, but the mutex might using var m = new Mutex(true, "CreateUniqueUserId"); m.WaitOne(); try { - if (TryCreateUniqueIdentifierAndFile(uuidPath, out id)) - { - return id; - } + return CreateUniqueIdentifierAndFile(uuidPath); } finally { diff --git a/src/System.Management.Automation/utils/tracing/EtwActivity.cs b/src/System.Management.Automation/utils/tracing/EtwActivity.cs index b8b4ac330f9..dfe642ee840 100644 --- a/src/System.Management.Automation/utils/tracing/EtwActivity.cs +++ b/src/System.Management.Automation/utils/tracing/EtwActivity.cs @@ -474,10 +474,7 @@ protected void WriteEvent(EventDescriptor ed, params object[] payload) } bool success = provider.WriteEvent(in ed, payload); - if (EventWritten != null) - { - EventWritten.Invoke(this, new EtwEventArgs(ed, success, payload)); - } + EventWritten?.Invoke(this, new EtwEventArgs(ed, success, payload)); } private EventProvider GetProvider() diff --git a/src/System.Management.Automation/utils/tracing/PSSysLogProvider.cs b/src/System.Management.Automation/utils/tracing/PSSysLogProvider.cs index 011d7577239..5a55d2f4cc1 100755 --- a/src/System.Management.Automation/utils/tracing/PSSysLogProvider.cs +++ b/src/System.Management.Automation/utils/tracing/PSSysLogProvider.cs @@ -49,11 +49,8 @@ private static StringBuilder PayloadBuilder { get { - if (t_payloadBuilder == null) - { - // NOTE: Thread static fields must be explicitly initialized for each thread. - t_payloadBuilder = new StringBuilder(200); - } + // NOTE: Thread static fields must be explicitly initialized for each thread. + t_payloadBuilder ??= new StringBuilder(200); return t_payloadBuilder; } diff --git a/src/System.Management.Automation/utils/tracing/SysLogProvider.cs b/src/System.Management.Automation/utils/tracing/SysLogProvider.cs index 27d0e133fd5..4b084504718 100755 --- a/src/System.Management.Automation/utils/tracing/SysLogProvider.cs +++ b/src/System.Management.Automation/utils/tracing/SysLogProvider.cs @@ -130,11 +130,8 @@ private static StringBuilder MessageBuilder { get { - if (t_messageBuilder == null) - { - // NOTE: Thread static fields must be explicitly initialized for each thread. - t_messageBuilder = new StringBuilder(200); - } + // NOTE: Thread static fields must be explicitly initialized for each thread. + t_messageBuilder ??= new StringBuilder(200); return t_messageBuilder; } @@ -201,10 +198,7 @@ private bool ShouldLog(PSLevel level, PSKeyword keywords, PSChannel channel) { get { - if (_resourceManager is null) - { - _resourceManager = new global::System.Resources.ResourceManager("System.Management.Automation.resources.EventResource", typeof(EventResource).Assembly); - } + _resourceManager ??= new global::System.Resources.ResourceManager("System.Management.Automation.resources.EventResource", typeof(EventResource).Assembly); return _resourceManager; } @@ -381,7 +375,7 @@ internal static class NativeMethods /// See man 3 syslog for more info. /// /// - /// The OR of a priority and facility in the SysLogPriority enum indicating the the priority and facility of the log entry. + /// The OR of a priority and facility in the SysLogPriority enum indicating the priority and facility of the log entry. /// /// The message to put in the log entry. [DllImport(libpslnative, CharSet = CharSet.Ansi, EntryPoint = "Native_SysLog")] diff --git a/src/TypeCatalogGen/TypeCatalogGen.csproj b/src/TypeCatalogGen/TypeCatalogGen.csproj index e81e027ea75..170819a6bf9 100644 --- a/src/TypeCatalogGen/TypeCatalogGen.csproj +++ b/src/TypeCatalogGen/TypeCatalogGen.csproj @@ -2,7 +2,7 @@ Generates CorePsTypeCatalog.cs given powershell.inc - net6.0 + net7.0 true TypeCatalogGen Exe diff --git a/src/powershell-native/Install-PowerShellRemoting.ps1 b/src/powershell-native/Install-PowerShellRemoting.ps1 index f42fcde2da6..ea164367af3 100644 --- a/src/powershell-native/Install-PowerShellRemoting.ps1 +++ b/src/powershell-native/Install-PowerShellRemoting.ps1 @@ -11,7 +11,7 @@ # If no parameter is specified, it means that this instance of PowerShell is registering itself. # # Assumptions: -# 1. The CoreCLR and the the PowerShell assemblies are side-by-side in $PSHOME +# 1. The CoreCLR and the PowerShell assemblies are side-by-side in $PSHOME # 2. Plugins are registered by version number. Only one plugin can be automatically registered # per PowerShell version. However, multiple endpoints may be manually registered for a given # plugin. @@ -188,7 +188,7 @@ function Install-PluginEndpoint { $pluginPath = Join-Path $resolvedPluginAbsolutePath "pwrshplugin.dll" - # This is forced to ensure the the file is placed correctly + # This is forced to ensure the file is placed correctly Copy-Item $targetPsHome\pwrshplugin.dll $resolvedPluginAbsolutePath -Force -Verbose -ErrorAction Stop $pluginFile = Join-Path $resolvedPluginAbsolutePath "RemotePowerShellConfig.txt" diff --git a/src/powershell-win-core/powershell-win-core.csproj b/src/powershell-win-core/powershell-win-core.csproj index 16f625827d4..9879e8485b4 100644 --- a/src/powershell-win-core/powershell-win-core.csproj +++ b/src/powershell-win-core/powershell-win-core.csproj @@ -29,7 +29,7 @@ PreserveNewest PreserveNewest - + PreserveNewest PreserveNewest diff --git a/src/powershell/Program.cs b/src/powershell/Program.cs index 3fee683618f..d085db22b98 100644 --- a/src/powershell/Program.cs +++ b/src/powershell/Program.cs @@ -5,6 +5,7 @@ using System; using System.IO; +using System.Management.Automation; using System.Reflection; using System.Runtime.InteropServices; @@ -89,7 +90,7 @@ private static void AttemptExecPwshLogin(string[] args) return; } - bool isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + bool isLinux = Platform.IsLinux; // The first byte (ASCII char) of the name of this process, used to detect '-' for login byte procNameFirstByte; diff --git a/stylecop.json b/stylecop.json index 77517f2095b..9653436690c 100644 --- a/stylecop.json +++ b/stylecop.json @@ -25,7 +25,7 @@ }, "namingRules" : { "allowCommonHungarianPrefixes" : true, - "allowedHungarianPrefixes" : [ "n", "r", "l", "i", "io", "is", "fs", "lp", "dw", "h", "rs", "ps", "op", "sb", "my" ] + "allowedHungarianPrefixes" : [ "n", "r", "l", "i", "io", "is", "fs", "lp", "dw", "h", "rs", "ps", "op", "sb", "my", "vt" ] }, "maintainabilityRules" : { "topLevelTypes" : [ diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index 7383cc2b8a1..1aa87ba3d69 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -179,6 +179,13 @@ Describe "SSHRemoting Basic Tests" -tags CI { Write-Verbose -Verbose "It Complete" } + It "Verifies explicit Options parameter" { + $options = @{"Port"="22"} + $script:session = New-PSSession -HostName localhost -Options $options -ErrorVariable err + $err | Should -HaveCount 0 + VerifySession $script:session + } + It "Verifies explicit Subsystem parameter" { Write-Verbose -Verbose "It Starting: Verifies explicit Subsystem parameter" $portNum = 22 @@ -223,6 +230,25 @@ Describe "SSHRemoting Basic Tests" -tags CI { VerifySession $script:sessions[1] Write-Verbose -Verbose "It Complete" } + + It "Verifies the 'pwshconfig' configured endpoint." { + Write-Verbose -Verbose "It Starting: Verifies the 'pwshconfig' configured endpoint." + $script:session = TryNewPSSession -HostName localhost -Subsystem 'pwshconfig' + $script:session | Should -Not -BeNullOrEmpty + # Configured session should be in ConstrainedLanguage mode. + $sessionLangMode = Invoke-Command -Session $script:session -ScriptBlock { "$($ExecutionContext.SessionState.LanguageMode)" } + $sessionLangMode | Should -BeExactly "ConstrainedLanguage" + Write-Verbose -Verbose "It Complete" + } + + <# + It "Verifes that 'pwshbroken' throws expected error for missing config file." { + Write-Verbose -Verbose "It Starting: Verifes that 'pwshbroken' throws expected error for missing config file." + { $script:session = TryNewPSSession -HostName localhost -Subsystem 'pwshbroken' } | Should -Throw + $script:session = $null + Write-Verbose -Verbose "It Complete" + } + #> } function TryCreateRunspace @@ -263,7 +289,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { } } - if ($null -eq $rs) + if (($null -eq $rs) -or !($rs -is [runspace])) { $message = "Runspace open unable to connect to SSH remoting endpoint after two attempts. Error: $($connectionError.Message)" throw [System.Management.Automation.PSInvalidOperationException]::new($message) @@ -312,7 +338,7 @@ Describe "SSHRemoting Basic Tests" -tags CI { AfterEach { Write-Verbose -Verbose "Starting Runspace close AfterEach" - if ($script:rs -ne $null) { $script:rs.Dispose() } + if (($script:rs -ne $null) -and ($script:rs -is [runspace])) { $script:rs.Dispose() } Write-Verbose -Verbose "AfterEach complete" } diff --git a/test/Test.Common.props b/test/Test.Common.props index a1b4b8686d6..84424241e72 100644 --- a/test/Test.Common.props +++ b/test/Test.Common.props @@ -6,7 +6,7 @@ Microsoft Corporation (c) Microsoft Corporation. - net6.0 + net7.0 9.0 true @@ -21,7 +21,7 @@ - true + false diff --git a/test/common/markdown/markdown-link.tests.ps1 b/test/common/markdown-link/markdown-link.tests.ps1 similarity index 92% rename from test/common/markdown/markdown-link.tests.ps1 rename to test/common/markdown-link/markdown-link.tests.ps1 index 4339c0174ae..96c101afd4a 100644 --- a/test/common/markdown/markdown-link.tests.ps1 +++ b/test/common/markdown-link/markdown-link.tests.ps1 @@ -25,7 +25,18 @@ Describe "Verify Markdown Links" { Get-Job | Remove-Job -Force } - $groups = Get-ChildItem -Path "$PSScriptRoot\..\..\..\*.md" -Recurse | Where-Object {$_.DirectoryName -notlike '*node_modules*'} | Group-Object -Property directory + $gciParams = @{} + if ($env:MARKDOWN_FOLDER) { + $gciParams["Path"] = (Join-Path -Path $env:MARKDOWN_FOLDER -ChildPath '*.md') + } else { + $gciParams["Path"] = "$PSScriptRoot\..\..\..\*.md" + } + + if ($env:MARKDOWN_RECURSE -ne 'False') { + $gciParams["Recurse"] = $true + } + + $groups = Get-ChildItem @gciParams | Where-Object {$_.DirectoryName -notlike '*node_modules*'} | Group-Object -Property directory $jobs = @{} # start all link verification in parallel diff --git a/test/common/markdown/.gitignore b/test/common/markdown-lint/.gitignore similarity index 100% rename from test/common/markdown/.gitignore rename to test/common/markdown-lint/.gitignore diff --git a/test/common/markdown/gulpfile.js b/test/common/markdown-lint/gulpfile.js similarity index 100% rename from test/common/markdown/gulpfile.js rename to test/common/markdown-lint/gulpfile.js diff --git a/test/common/markdown/markdown.tests.ps1 b/test/common/markdown-lint/markdown.tests.ps1 similarity index 100% rename from test/common/markdown/markdown.tests.ps1 rename to test/common/markdown-lint/markdown.tests.ps1 diff --git a/test/common/markdown/package.json b/test/common/markdown-lint/package.json similarity index 89% rename from test/common/markdown/package.json rename to test/common/markdown-lint/package.json index 7ce4cd73500..f04a98e44fa 100644 --- a/test/common/markdown/package.json +++ b/test/common/markdown-lint/package.json @@ -6,7 +6,7 @@ "main": "gulpfile.js", "dependencies": { "gulp": "^4.0.2", - "markdownlint": "^0.22.0", + "markdownlint": "^0.25.1", "through2": "^4.0.2" }, "resolutions": { @@ -15,7 +15,8 @@ "ini": "^1.3.6", "copy-props":"^2.0.5", "glob-parent":"^5.1.2", - "hosted-git-info":"^3.0.8" + "hosted-git-info":"^3.0.8", + "set-value":"^4.0.1" }, "devDependencies": { "gulp-concat": "^2.6.1", diff --git a/test/common/markdown/yarn.lock b/test/common/markdown-lint/yarn.lock similarity index 98% rename from test/common/markdown/yarn.lock rename to test/common/markdown-lint/yarn.lock index 2402a0450ec..1549bed02b4 100644 --- a/test/common/markdown/yarn.lock +++ b/test/common/markdown-lint/yarn.lock @@ -528,10 +528,10 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" -entities@~2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz" - integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ== +entities@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" + integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== error-ex@^1.2.0: version "1.3.2" @@ -1180,6 +1180,11 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== +is-primitive@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-3.0.1.tgz#98c4db1abff185485a657fc2905052b940524d05" + integrity sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w== + is-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz" @@ -1357,23 +1362,23 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -markdown-it@12.0.2: - version "12.0.2" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.0.2.tgz#4401beae8df8aa2221fc6565a7188e60a06ef0ed" - integrity sha512-4Lkvjbv2kK+moL9TbeV+6/NHx+1Q+R/NIdUlFlkqkkzUcTod4uiyTJRiBidKR9qXSdkNFkgv+AELY8KN9vSgVA== +markdown-it@12.3.2: + version "12.3.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90" + integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg== dependencies: argparse "^2.0.1" - entities "~2.0.0" + entities "~2.1.0" linkify-it "^3.0.1" mdurl "^1.0.1" uc.micro "^1.0.5" -markdownlint@^0.22.0: - version "0.22.0" - resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.22.0.tgz#4ed95b61c17ae9f4dfca6a01f038c744846c0a72" - integrity sha512-J4B+iMc12pOdp/wfYi03W2qfAfEyiZzq3qvQh/8vOMNU8vXYY6Jg440EY7dWTBCqROhb1i4nAn3BTByJ5kdx1w== +markdownlint@^0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.25.1.tgz#df04536607ebeeda5ccd5e4f38138823ed623788" + integrity sha512-AG7UkLzNa1fxiOv5B+owPsPhtM4D6DoODhsJgiaNg1xowXovrYgOnLqAgOOFQpWOlHFVQUzjMY5ypNNTeov92g== dependencies: - markdown-it "12.0.2" + markdown-it "12.3.2" matchdep@^2.0.0: version "2.0.0" @@ -1629,9 +1634,9 @@ path-is-absolute@^1.0.0: integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-root-regex@^0.1.0: version "0.1.2" @@ -1896,15 +1901,13 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== +set-value@^2.0.0, set-value@^2.0.1, set-value@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-4.1.0.tgz#aa433662d87081b75ad88a4743bd450f044e7d09" + integrity sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw== dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" + is-plain-object "^2.0.4" + is-primitive "^3.0.1" snapdragon-node@^2.0.1: version "2.1.1" @@ -1993,7 +1996,7 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz" integrity sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw== -split-string@^3.0.1, split-string@^3.0.2: +split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz" integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== diff --git a/test/docker/networktest/Dockerfile b/test/docker/networktest/Dockerfile index 1e4f3d65ebb..cac78654c86 100644 --- a/test/docker/networktest/Dockerfile +++ b/test/docker/networktest/Dockerfile @@ -1,5 +1,5 @@ # escape=` -FROM microsoft/windowsservercore +FROM mcr.microsoft.com/windows/servercore:ltsc2022 SHELL ["pwsh.exe","-command"] diff --git a/test/hosting/test_HostingBasic.cs b/test/hosting/test_HostingBasic.cs index 6cff7978e48..e29b14e38c0 100644 --- a/test/hosting/test_HostingBasic.cs +++ b/test/hosting/test_HostingBasic.cs @@ -183,6 +183,19 @@ public static void TestConsoleShellScenario() Assert.Equal(42, ret); } + /* Test disabled because CommandLineParser is static and can only be intialized once (above in TestConsoleShellScenario) + /// + /// ConsoleShell cannot start with both InitialSessionState and -ConfigurationFile argument configurations specified. + /// + [Fact] + public static void TestConsoleShellConfigConflictError() + { + var iss = System.Management.Automation.Runspaces.InitialSessionState.CreateDefault2(); + int ret = ConsoleShell.Start(iss, "BannerText", string.Empty, new string[] { @"-ConfigurationFile ""noneSuch""" }); + Assert.Equal(70, ret); // ExitCodeInitFailure. + } + */ + [Fact] public static void TestBuiltInModules() { diff --git a/test/packaging/windows/msi.tests.ps1 b/test/packaging/windows/msi.tests.ps1 index 9d1c218c4c0..0ee34ca482c 100644 --- a/test/packaging/windows/msi.tests.ps1 +++ b/test/packaging/windows/msi.tests.ps1 @@ -39,7 +39,7 @@ Describe -Name "Windows MSI" -Fixture { } function Get-UseMU { - $useMu = 0 + $useMu = $null $key = 'HKLM:\SOFTWARE\Microsoft\PowerShellCore\' if ($runtime -like '*x86*') { $key = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\PowerShellCore\' @@ -49,6 +49,25 @@ Describe -Name "Windows MSI" -Fixture { $useMu = Get-ItemPropertyValue -Path $key -Name UseMU -ErrorAction SilentlyContinue } catch {} + if (!$useMu) { + $useMu = 0 + } + + return $useMu + } + + function Set-UseMU { + param( + [int] + $Value + ) + $key = 'HKLM:\SOFTWARE\Microsoft\PowerShellCore\' + if ($runtime -like '*x86*') { + $key = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\PowerShellCore\' + } + + Set-ItemProperty -Path $key -Name UseMU -Value $Value -Type DWord + return $useMu } @@ -185,6 +204,15 @@ Describe -Name "Windows MSI" -Fixture { } Context "Add Path disabled" { + BeforeAll { + Set-UseMU -Value 0 + } + + It "UseMU should be 0 before install" -Skip:(!(Test-Elevated)) { + $useMu = Get-UseMU + $useMu | Should -Be 0 + } + It "MSI should install without error" -Skip:(!(Test-Elevated)) { { Invoke-MsiExec -Install -MsiPath $msiX64Path -Properties @{ADD_PATH = 0; USE_MU = 1; ENABLE_MU = 1} @@ -213,6 +241,15 @@ Describe -Name "Windows MSI" -Fixture { } Context "USE_MU disabled" { + BeforeAll { + Set-UseMU -Value 0 + } + + It "UseMU should be 0 before install" -Skip:(!(Test-Elevated)) { + $useMu = Get-UseMU + $useMu | Should -Be 0 + } + It "MSI should install without error" -Skip:(!(Test-Elevated)) { { Invoke-MsiExec -Install -MsiPath $msiX64Path -Properties @{USE_MU = 0} diff --git a/test/perf/benchmarks/Engine.Compiler.cs b/test/perf/benchmarks/Engine.Compiler.cs new file mode 100644 index 00000000000..10e0054a01c --- /dev/null +++ b/test/perf/benchmarks/Engine.Compiler.cs @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#if NET6_0 + +using System; +using System.Collections.Generic; +using System.IO; +using System.Management.Automation; +using System.Management.Automation.Language; + +using BenchmarkDotNet.Attributes; +using MicroBenchmarks; + +namespace Engine +{ + [BenchmarkCategory(Categories.Engine, Categories.Internal)] + public class Compiler + { + private static readonly Dictionary s_scriptBlocksDict; + private static readonly List s_functionNames; + private ScriptBlockAst _currentAst; + + static Compiler() + { + string pattern = string.Format("{0}test{0}perf{0}benchmarks", Path.DirectorySeparatorChar); + string location = typeof(Compiler).Assembly.Location; + string testFilePath = null; + + int start = location.IndexOf(pattern, StringComparison.Ordinal); + if (start > 0) + { + testFilePath = Path.Join(location.AsSpan(0, start + pattern.Length), "assets", "compiler.test.ps1"); + } + + var topScriptBlockAst = Parser.ParseFile(testFilePath, tokens: out _, errors: out _); + var allFunctions = topScriptBlockAst.FindAll(ast => ast is FunctionDefinitionAst, searchNestedScriptBlocks: false); + + s_scriptBlocksDict = new Dictionary(capacity: 16); + s_functionNames = new List(capacity: 16); + + foreach (FunctionDefinitionAst function in allFunctions) + { + s_functionNames.Add(function.Name); + s_scriptBlocksDict.Add(function.Name, function.Body); + } + } + + [ParamsSource(nameof(FunctionName))] + public string FunctionsToCompile { get; set; } + + public IEnumerable FunctionName() => s_functionNames; + + [GlobalSetup(Target = nameof(CompileFunction))] + public void GlobalSetup() + { + _currentAst = s_scriptBlocksDict[FunctionsToCompile]; + + // Run it once to get the C# code jitted. + // The first call to this takes relatively too long, which makes the BDN's heuristic incorrectly + // believe that there is no need to run many ops in each interation. However, the subsequent runs + // of this method is much faster than the first run, and this causes 'MinIterationTime' warnings + // to our benchmarks and make the benchmark results not reliable. + // Calling this method once in 'GlobalSetup' is a workaround. + // See https://github.com/dotnet/BenchmarkDotNet/issues/837#issuecomment-828600157 + CompileFunction(); + } + + [Benchmark] + public bool CompileFunction() + { + var compiledData = new CompiledScriptBlockData(_currentAst, isFilter: false); + return compiledData.Compile(true); + } + } +} + +#endif diff --git a/test/perf/benchmarks/Engine.ScriptBlock.cs b/test/perf/benchmarks/Engine.ScriptBlock.cs index ad373dd5f99..1f9ac989428 100644 --- a/test/perf/benchmarks/Engine.ScriptBlock.cs +++ b/test/perf/benchmarks/Engine.ScriptBlock.cs @@ -36,7 +36,7 @@ public IEnumerable ValuesForScript() yield return @"[System.IO.Path]::HasExtension('')"; // Test on COM method invocation. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (Platform.IsWindows) { yield return @"$sh=New-Object -ComObject Shell.Application; $sh.Namespace('c:\')"; yield return @"$fs=New-Object -ComObject scripting.filesystemobject; $fs.Drives"; @@ -54,7 +54,7 @@ public void GlobalSetup() // believe that there is no need to run many ops in each interation. However, the subsequent runs // of this method is much faster than the first run, and this causes 'MinIterationTime' warnings // to our benchmarks and make the benchmark results not reliable. - // Calling this method once in 'GlobalSetup' is a workaround. + // Calling this method once in 'GlobalSetup' is a workaround. // See https://github.com/dotnet/BenchmarkDotNet/issues/837#issuecomment-828600157 scriptBlock.Invoke(); } diff --git a/test/perf/benchmarks/Program.cs b/test/perf/benchmarks/Program.cs index 2b3aafdb127..53f9a3ce95b 100644 --- a/test/perf/benchmarks/Program.cs +++ b/test/perf/benchmarks/Program.cs @@ -10,7 +10,7 @@ namespace MicroBenchmarks { - public class Program + public sealed class Program { public static int Main(string[] args) { diff --git a/test/perf/benchmarks/assets/compiler.test.ps1 b/test/perf/benchmarks/assets/compiler.test.ps1 new file mode 100644 index 00000000000..5105ae9b408 --- /dev/null +++ b/test/perf/benchmarks/assets/compiler.test.ps1 @@ -0,0 +1,2685 @@ +## Copyright (c) Microsoft Corporation. +## Licensed under the MIT License. + +function Get-EnvInformation +{ + $environment = @{'IsWindows' = [System.Environment]::OSVersion.Platform -eq [System.PlatformID]::Win32NT} + # PowerShell will likely not be built on pre-1709 nanoserver + if ('System.Management.Automation.Platform' -as [type]) { + $environment += @{'IsCoreCLR' = [System.Management.Automation.Platform]::IsCoreCLR} + $environment += @{'IsLinux' = [System.Management.Automation.Platform]::IsLinux} + $environment += @{'IsMacOS' = [System.Management.Automation.Platform]::IsMacOS} + } else { + $environment += @{'IsCoreCLR' = $false} + $environment += @{'IsLinux' = $false} + $environment += @{'IsMacOS' = $false} + } + + if ($environment.IsWindows) + { + $environment += @{'IsAdmin' = (New-Object Security.Principal.WindowsPrincipal ([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)} + $environment += @{'nugetPackagesRoot' = "${env:USERPROFILE}\.nuget\packages", "${env:NUGET_PACKAGES}"} + } + else + { + $environment += @{'nugetPackagesRoot' = "${env:HOME}/.nuget/packages"} + } + + if ($environment.IsMacOS) { + $environment += @{'UsingHomebrew' = [bool](Get-Command brew -ErrorAction ignore)} + $environment += @{'UsingMacports' = [bool](Get-Command port -ErrorAction ignore)} + + $environment += @{ + 'OSArchitecture' = if ((uname -v) -match 'ARM64') { 'arm64' } else { 'x64' } + } + + if (-not($environment.UsingHomebrew -or $environment.UsingMacports)) { + throw "Neither Homebrew nor MacPorts is installed on this system, visit https://brew.sh/ or https://www.macports.org/ to continue" + } + } + + if ($environment.IsLinux) { + $LinuxInfo = Get-Content /etc/os-release -Raw | ConvertFrom-StringData + $lsb_release = Get-Command lsb_release -Type Application -ErrorAction Ignore | Select-Object -First 1 + if ($lsb_release) { + $LinuxID = & $lsb_release -is + } + else { + $LinuxID = "" + } + + $environment += @{'LinuxInfo' = $LinuxInfo} + $environment += @{'IsDebian' = $LinuxInfo.ID -match 'debian' -or $LinuxInfo.ID -match 'kali'} + $environment += @{'IsDebian9' = $environment.IsDebian -and $LinuxInfo.VERSION_ID -match '9'} + $environment += @{'IsDebian10' = $environment.IsDebian -and $LinuxInfo.VERSION_ID -match '10'} + $environment += @{'IsDebian11' = $environment.IsDebian -and $LinuxInfo.PRETTY_NAME -match 'bullseye'} + $environment += @{'IsUbuntu' = $LinuxInfo.ID -match 'ubuntu' -or $LinuxID -match 'Ubuntu'} + $environment += @{'IsUbuntu16' = $environment.IsUbuntu -and $LinuxInfo.VERSION_ID -match '16.04'} + $environment += @{'IsUbuntu18' = $environment.IsUbuntu -and $LinuxInfo.VERSION_ID -match '18.04'} + $environment += @{'IsUbuntu20' = $environment.IsUbuntu -and $LinuxInfo.VERSION_ID -match '20.04'} + $environment += @{'IsCentOS' = $LinuxInfo.ID -match 'centos' -and $LinuxInfo.VERSION_ID -match '7'} + $environment += @{'IsFedora' = $LinuxInfo.ID -match 'fedora' -and $LinuxInfo.VERSION_ID -ge 24} + $environment += @{'IsOpenSUSE' = $LinuxInfo.ID -match 'opensuse'} + $environment += @{'IsSLES' = $LinuxInfo.ID -match 'sles'} + $environment += @{'IsRedHat' = $LinuxInfo.ID -match 'rhel'} + $environment += @{'IsRedHat7' = $environment.IsRedHat -and $LinuxInfo.VERSION_ID -match '7' } + $environment += @{'IsOpenSUSE13' = $environment.IsOpenSUSE -and $LinuxInfo.VERSION_ID -match '13'} + $environment += @{'IsOpenSUSE42.1' = $environment.IsOpenSUSE -and $LinuxInfo.VERSION_ID -match '42.1'} + $environment += @{'IsDebianFamily' = $environment.IsDebian -or $environment.IsUbuntu} + $environment += @{'IsRedHatFamily' = $environment.IsCentOS -or $environment.IsFedora -or $environment.IsRedHat} + $environment += @{'IsSUSEFamily' = $environment.IsSLES -or $environment.IsOpenSUSE} + $environment += @{'IsAlpine' = $LinuxInfo.ID -match 'alpine'} + + # Workaround for temporary LD_LIBRARY_PATH hack for Fedora 24 + # https://github.com/PowerShell/PowerShell/issues/2511 + if ($environment.IsFedora -and (Test-Path ENV:\LD_LIBRARY_PATH)) { + Remove-Item -Force ENV:\LD_LIBRARY_PATH + Get-ChildItem ENV: + } + + if( -not( + $environment.IsDebian -or + $environment.IsUbuntu -or + $environment.IsRedHatFamily -or + $environment.IsSUSEFamily -or + $environment.IsAlpine) + ) { + if ($SkipLinuxDistroCheck) { + Write-Warning "The current OS : $($LinuxInfo.ID) is not supported for building PowerShell." + } else { + throw "The current OS : $($LinuxInfo.ID) is not supported for building PowerShell. Import this module with '-ArgumentList `$true' to bypass this check." + } + } + } + + return [PSCustomObject] $environment +} + +function Start-PSBuild { + [CmdletBinding(DefaultParameterSetName="Default")] + param( + # When specified this switch will stops running dev powershell + # to help avoid compilation error, because file are in use. + [switch]$StopDevPowerShell, + + [switch]$Restore, + # Accept a path to the output directory + # When specified, --output will be passed to dotnet + [string]$Output, + [switch]$ResGen, + [switch]$TypeGen, + [switch]$Clean, + [Parameter(ParameterSetName="Legacy")] + [switch]$PSModuleRestore, + [Parameter(ParameterSetName="Default")] + [switch]$NoPSModuleRestore, + [switch]$CI, + [switch]$ForMinimalSize, + + # Skips the step where the pwsh that's been built is used to create a configuration + # Useful when changing parsing/compilation, since bugs there can mean we can't get past this step + [switch]$SkipExperimentalFeatureGeneration, + + # this switch will re-build only System.Management.Automation.dll + # it's useful for development, to do a quick changes in the engine + [switch]$SMAOnly, + + # These runtimes must match those in project.json + # We do not use ValidateScript since we want tab completion + # If this parameter is not provided it will get determined automatically. + [ValidateSet("alpine-x64", + "fxdependent", + "fxdependent-win-desktop", + "linux-arm", + "linux-arm64", + "linux-x64", + "osx-arm64", + "osx-x64", + "win-arm", + "win-arm64", + "win7-x64", + "win7-x86")] + [string]$Runtime, + + [ValidateSet('Debug', 'Release', 'CodeCoverage', '')] # We might need "Checked" as well + [string]$Configuration, + + [switch]$CrossGen, + + [ValidatePattern("^v\d+\.\d+\.\d+(-\w+(\.\d{1,2})?)?$")] + [ValidateNotNullOrEmpty()] + [string]$ReleaseTag, + [switch]$Detailed, + [switch]$InteractiveAuth, + [switch]$SkipRoslynAnalyzers + ) + + if ($ReleaseTag -and $ReleaseTag -notmatch "^v\d+\.\d+\.\d+(-(preview|rc)(\.\d{1,2})?)?$") { + Write-Warning "Only preview or rc are supported for releasing pre-release version of PowerShell" + } + + if ($PSCmdlet.ParameterSetName -eq "Default" -and !$NoPSModuleRestore) + { + $PSModuleRestore = $true + } + + if ($Runtime -eq "linux-arm" -and $environment.IsLinux -and -not $environment.IsUbuntu) { + throw "Cross compiling for linux-arm is only supported on Ubuntu environment" + } + + if ("win-arm","win-arm64" -contains $Runtime -and -not $environment.IsWindows) { + throw "Cross compiling for win-arm or win-arm64 is only supported on Windows environment" + } + + if ($ForMinimalSize) { + if ($CrossGen) { + throw "Build for the minimal size requires the minimal disk footprint, so `CrossGen` is not allowed" + } + + if ($Runtime -and "linux-x64", "win7-x64", "osx-x64" -notcontains $Runtime) { + throw "Build for the minimal size is enabled only for following runtimes: 'linux-x64', 'win7-x64', 'osx-x64'" + } + } + + function Stop-DevPowerShell { + Get-Process pwsh* | + Where-Object { + $_.Modules | + Where-Object { + $_.FileName -eq (Resolve-Path $script:Options.Output).Path + } + } | + Stop-Process -Verbose + } + + if ($Clean) { + Write-Log -message "Cleaning your working directory. You can also do it with 'git clean -fdX --exclude .vs/PowerShell/v16/Server/sqlite3'" + Push-Location $PSScriptRoot + try { + # Excluded sqlite3 folder is due to this Roslyn issue: https://github.com/dotnet/roslyn/issues/23060 + # Excluded src/Modules/nuget.config as this is required for release build. + # Excluded nuget.config as this is required for release build. + git clean -fdX --exclude .vs/PowerShell/v16/Server/sqlite3 --exclude src/Modules/nuget.config --exclude nuget.config + } finally { + Pop-Location + } + } + + # Add .NET CLI tools to PATH + Find-Dotnet + + # Verify we have git in place to do the build, and abort if the precheck failed + $precheck = precheck 'git' "Build dependency 'git' not found in PATH. See " + if (-not $precheck) { + return + } + + # Verify we have .NET SDK in place to do the build, and abort if the precheck failed + $precheck = precheck 'dotnet' "Build dependency 'dotnet' not found in PATH. Run Start-PSBootstrap. Also see " + if (-not $precheck) { + return + } + + # Verify if the dotnet in-use is the required version + $dotnetCLIInstalledVersion = Start-NativeExecution -sb { dotnet --version } -IgnoreExitcode + If ($dotnetCLIInstalledVersion -ne $dotnetCLIRequiredVersion) { + Write-Warning @" +The currently installed .NET Command Line Tools is not the required version. + +Installed version: $dotnetCLIInstalledVersion +Required version: $dotnetCLIRequiredVersion + +Fix steps: + +1. Remove the installed version from: + - on windows '`$env:LOCALAPPDATA\Microsoft\dotnet' + - on macOS and linux '`$env:HOME/.dotnet' +2. Run Start-PSBootstrap or Install-Dotnet +3. Start-PSBuild -Clean +`n +"@ + return + } + + # set output options + $OptionsArguments = @{ + CrossGen=$CrossGen + Output=$Output + Runtime=$Runtime + Configuration=$Configuration + Verbose=$true + SMAOnly=[bool]$SMAOnly + PSModuleRestore=$PSModuleRestore + ForMinimalSize=$ForMinimalSize + } + $script:Options = New-PSOptions @OptionsArguments + + if ($StopDevPowerShell) { + Stop-DevPowerShell + } + + # setup arguments + # adding ErrorOnDuplicatePublishOutputFiles=false due to .NET SDk issue: https://github.com/dotnet/sdk/issues/15748 + # removing --no-restore due to .NET SDK issue: https://github.com/dotnet/sdk/issues/18999 + # $Arguments = @("publish","--no-restore","/property:GenerateFullPaths=true", "/property:ErrorOnDuplicatePublishOutputFiles=false") + $Arguments = @("publish","/property:GenerateFullPaths=true", "/property:ErrorOnDuplicatePublishOutputFiles=false") + if ($Output -or $SMAOnly) { + $Arguments += "--output", (Split-Path $Options.Output) + } + + # Add --self-contained due to "warning NETSDK1179: One of '--self-contained' or '--no-self-contained' options are required when '--runtime' is used." + if ($Options.Runtime -like 'fxdependent*') { + $Arguments += "--no-self-contained" + } + else { + $Arguments += "--self-contained" + } + + if ($Options.Runtime -like 'win*' -or ($Options.Runtime -like 'fxdependent*' -and $environment.IsWindows)) { + $Arguments += "/property:IsWindows=true" + } + else { + $Arguments += "/property:IsWindows=false" + } + + # Framework Dependent builds do not support ReadyToRun as it needs a specific runtime to optimize for. + # The property is set in Powershell.Common.props file. + # We override the property through the build command line. + if($Options.Runtime -like 'fxdependent*' -or $ForMinimalSize) { + $Arguments += "/property:PublishReadyToRun=false" + } + + $Arguments += "--configuration", $Options.Configuration + $Arguments += "--framework", $Options.Framework + + if ($Detailed.IsPresent) + { + $Arguments += '--verbosity', 'd' + } + + if (-not $SMAOnly -and $Options.Runtime -notlike 'fxdependent*') { + # libraries should not have runtime + $Arguments += "--runtime", $Options.Runtime + } + + if ($ReleaseTag) { + $ReleaseTagToUse = $ReleaseTag -Replace '^v' + $Arguments += "/property:ReleaseTag=$ReleaseTagToUse" + } + + if ($SkipRoslynAnalyzers) { + $Arguments += "/property:RunAnalyzersDuringBuild=false" + } + + # handle Restore + Restore-PSPackage -Options $Options -Force:$Restore -InteractiveAuth:$InteractiveAuth + + # handle ResGen + # Heuristic to run ResGen on the fresh machine + if ($ResGen -or -not (Test-Path "$PSScriptRoot/src/Microsoft.PowerShell.ConsoleHost/gen")) { + Write-Log -message "Run ResGen (generating C# bindings for resx files)" + Start-ResGen + } + + # Handle TypeGen + # .inc file name must be different for Windows and Linux to allow build on Windows and WSL. + $incFileName = "powershell_$($Options.Runtime).inc" + if ($TypeGen -or -not (Test-Path "$PSScriptRoot/src/TypeCatalogGen/$incFileName")) { + Write-Log -message "Run TypeGen (generating CorePsTypeCatalog.cs)" + Start-TypeGen -IncFileName $incFileName + } + + # Get the folder path where pwsh.exe is located. + if ((Split-Path $Options.Output -Leaf) -like "pwsh*") { + $publishPath = Split-Path $Options.Output -Parent + } + else { + $publishPath = $Options.Output + } + + try { + # Relative paths do not work well if cwd is not changed to project + Push-Location $Options.Top + + if ($Options.Runtime -notlike 'fxdependent*') { + $sdkToUse = 'Microsoft.NET.Sdk' + if ($Options.Runtime -like 'win7-*' -and !$ForMinimalSize) { + ## WPF/WinForm and the PowerShell GraphicalHost assemblies are included + ## when 'Microsoft.NET.Sdk.WindowsDesktop' is used. + $sdkToUse = 'Microsoft.NET.Sdk.WindowsDesktop' + } + + $Arguments += "/property:SDKToUse=$sdkToUse" + + Write-Log -message "Run dotnet $Arguments from $PWD" + Start-NativeExecution { dotnet $Arguments } + Write-Log -message "PowerShell output: $($Options.Output)" + + if ($CrossGen) { + # fxdependent package cannot be CrossGen'ed + Start-CrossGen -PublishPath $publishPath -Runtime $script:Options.Runtime + Write-Log -message "pwsh.exe with ngen binaries is available at: $($Options.Output)" + } + } else { + $globalToolSrcFolder = Resolve-Path (Join-Path $Options.Top "../Microsoft.PowerShell.GlobalTool.Shim") | Select-Object -ExpandProperty Path + + if ($Options.Runtime -eq 'fxdependent') { + $Arguments += "/property:SDKToUse=Microsoft.NET.Sdk" + } elseif ($Options.Runtime -eq 'fxdependent-win-desktop') { + $Arguments += "/property:SDKToUse=Microsoft.NET.Sdk.WindowsDesktop" + } + + Write-Log -message "Run dotnet $Arguments from $PWD" + Start-NativeExecution { dotnet $Arguments } + Write-Log -message "PowerShell output: $($Options.Output)" + + try { + Push-Location $globalToolSrcFolder + $Arguments += "--output", $publishPath + Write-Log -message "Run dotnet $Arguments from $PWD to build global tool entry point" + Start-NativeExecution { dotnet $Arguments } + } + finally { + Pop-Location + } + } + } finally { + Pop-Location + } + + # No extra post-building task will run if '-SMAOnly' is specified, because its purpose is for a quick update of S.M.A.dll after full build. + if ($SMAOnly) { + return + } + + # publish reference assemblies + try { + Push-Location "$PSScriptRoot/src/TypeCatalogGen" + $refAssemblies = Get-Content -Path $incFileName | Where-Object { $_ -like "*microsoft.netcore.app*" } | ForEach-Object { $_.TrimEnd(';') } + $refDestFolder = Join-Path -Path $publishPath -ChildPath "ref" + + if (Test-Path $refDestFolder -PathType Container) { + Remove-Item $refDestFolder -Force -Recurse -ErrorAction Stop + } + New-Item -Path $refDestFolder -ItemType Directory -Force -ErrorAction Stop > $null + Copy-Item -Path $refAssemblies -Destination $refDestFolder -Force -ErrorAction Stop + } finally { + Pop-Location + } + + if ($ReleaseTag) { + $psVersion = $ReleaseTag + } + else { + $psVersion = git --git-dir="$PSScriptRoot/.git" describe + } + + if ($environment.IsLinux) { + if ($environment.IsRedHatFamily -or $environment.IsDebian) { + # Symbolic links added here do NOT affect packaging as we do not build on Debian. + # add two symbolic links to system shared libraries that libmi.so is dependent on to handle + # platform specific changes. This is the only set of platforms needed for this currently + # as Ubuntu has these specific library files in the platform and macOS builds for itself + # against the correct versions. + + if ($environment.IsDebian10 -or $environment.IsDebian11){ + $sslTarget = "/usr/lib/x86_64-linux-gnu/libssl.so.1.1" + $cryptoTarget = "/usr/lib/x86_64-linux-gnu/libcrypto.so.1.1" + } + elseif ($environment.IsDebian9){ + # NOTE: Debian 8 doesn't need these symlinks + $sslTarget = "/usr/lib/x86_64-linux-gnu/libssl.so.1.0.2" + $cryptoTarget = "/usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.2" + } + else { #IsRedHatFamily + $sslTarget = "/lib64/libssl.so.10" + $cryptoTarget = "/lib64/libcrypto.so.10" + } + + if ( ! (Test-Path "$publishPath/libssl.so.1.0.0")) { + $null = New-Item -Force -ItemType SymbolicLink -Target $sslTarget -Path "$publishPath/libssl.so.1.0.0" -ErrorAction Stop + } + if ( ! (Test-Path "$publishPath/libcrypto.so.1.0.0")) { + $null = New-Item -Force -ItemType SymbolicLink -Target $cryptoTarget -Path "$publishPath/libcrypto.so.1.0.0" -ErrorAction Stop + } + } + } + + # download modules from powershell gallery. + # - PowerShellGet, PackageManagement, Microsoft.PowerShell.Archive + if ($PSModuleRestore) { + Restore-PSModuleToBuild -PublishPath $publishPath + } + + # publish powershell.config.json + $config = @{} + if ($environment.IsWindows) { + $config = @{ "Microsoft.PowerShell:ExecutionPolicy" = "RemoteSigned"; + "WindowsPowerShellCompatibilityModuleDenyList" = @("PSScheduledJob","BestPractices","UpdateServices") } + } + + # When building preview, we want the configuration to enable all experiemental features by default + # ARM is cross compiled, so we can't run pwsh to enumerate Experimental Features + if (-not $SkipExperimentalFeatureGeneration -and + (Test-IsPreview $psVersion) -and + -not (Test-IsReleaseCandidate $psVersion) -and + -not $Runtime.Contains("arm") -and + -not ($Runtime -like 'fxdependent*')) { + + $json = & $publishPath\pwsh -noprofile -command { + # Special case for DSC code in PS; + # this experimental feature requires new DSC module that is not inbox, + # so we don't want default DSC use case be broken + [System.Collections.ArrayList] $expFeatures = Get-ExperimentalFeature | Where-Object Name -NE PS7DscSupport | ForEach-Object -MemberName Name + + $expFeatures | Out-String | Write-Verbose -Verbose + + # Make sure ExperimentalFeatures from modules in PSHome are added + # https://github.com/PowerShell/PowerShell/issues/10550 + $ExperimentalFeaturesFromGalleryModulesInPSHome = @() + $ExperimentalFeaturesFromGalleryModulesInPSHome | ForEach-Object { + if (!$expFeatures.Contains($_)) { + $null = $expFeatures.Add($_) + } + } + + ConvertTo-Json $expFeatures + } + + $config += @{ ExperimentalFeatures = ([string[]] ($json | ConvertFrom-Json)) } + } + + if ($config.Count -gt 0) { + $configPublishPath = Join-Path -Path $publishPath -ChildPath "powershell.config.json" + Set-Content -Path $configPublishPath -Value ($config | ConvertTo-Json) -Force -ErrorAction Stop + } + + # Restore the Pester module + if ($CI) { + Restore-PSPester -Destination (Join-Path $publishPath "Modules") + } +} + +function New-PSOptions { + [CmdletBinding()] + param( + [ValidateSet("Debug", "Release", "CodeCoverage", '')] + [string]$Configuration, + + [ValidateSet("net6.0")] + [string]$Framework = "net6.0", + + # These are duplicated from Start-PSBuild + # We do not use ValidateScript since we want tab completion + [ValidateSet("", + "alpine-x64", + "fxdependent", + "fxdependent-win-desktop", + "linux-arm", + "linux-arm64", + "linux-x64", + "osx-arm64", + "osx-x64", + "win-arm", + "win-arm64", + "win7-x64", + "win7-x86")] + [string]$Runtime, + + [switch]$CrossGen, + + # Accept a path to the output directory + # If not null or empty, name of the executable will be appended to + # this path, otherwise, to the default path, and then the full path + # of the output executable will be assigned to the Output property + [string]$Output, + + [switch]$SMAOnly, + + [switch]$PSModuleRestore, + + [switch]$ForMinimalSize + ) + + # Add .NET CLI tools to PATH + Find-Dotnet + + if (-not $Configuration) { + $Configuration = 'Debug' + } + + Write-Verbose "Using configuration '$Configuration'" + Write-Verbose "Using framework '$Framework'" + + if (-not $Runtime) { + if ($environment.IsLinux) { + $Runtime = "linux-x64" + } elseif ($environment.IsMacOS) { + if ($PSVersionTable.OS.Contains('ARM64')) { + $Runtime = "osx-arm64" + } + else { + $Runtime = "osx-x64" + } + } else { + $RID = dotnet --info | ForEach-Object { + if ($_ -match "RID") { + $_ -split "\s+" | Select-Object -Last 1 + } + } + + # We plan to release packages targeting win7-x64 and win7-x86 RIDs, + # which supports all supported windows platforms. + # So we, will change the RID to win7- + $Runtime = $RID -replace "win\d+", "win7" + } + + if (-not $Runtime) { + Throw "Could not determine Runtime Identifier, please update dotnet" + } else { + Write-Verbose "Using runtime '$Runtime'" + } + } + + $PowerShellDir = if ($Runtime -like 'win*' -or ($Runtime -like 'fxdependent*' -and $environment.IsWindows)) { + "powershell-win-core" + } else { + "powershell-unix" + } + + $Top = [IO.Path]::Combine($PSScriptRoot, "src", $PowerShellDir) + Write-Verbose "Top project directory is $Top" + + $Executable = if ($Runtime -like 'fxdependent*') { + "pwsh.dll" + } elseif ($environment.IsLinux -or $environment.IsMacOS) { + "pwsh" + } elseif ($environment.IsWindows) { + "pwsh.exe" + } + + # Build the Output path + if (!$Output) { + if ($Runtime -like 'fxdependent*') { + $Output = [IO.Path]::Combine($Top, "bin", $Configuration, $Framework, "publish", $Executable) + } else { + $Output = [IO.Path]::Combine($Top, "bin", $Configuration, $Framework, $Runtime, "publish", $Executable) + } + } else { + $Output = [IO.Path]::Combine($Output, $Executable) + } + + if ($SMAOnly) + { + $Top = [IO.Path]::Combine($PSScriptRoot, "src", "System.Management.Automation") + } + + $RootInfo = @{RepoPath = $PSScriptRoot} + + # the valid root is the root of the filesystem and the folder PowerShell + $RootInfo['ValidPath'] = Join-Path -Path ([system.io.path]::GetPathRoot($RootInfo.RepoPath)) -ChildPath 'PowerShell' + + if($RootInfo.RepoPath -ne $RootInfo.ValidPath) + { + $RootInfo['Warning'] = "Please ensure your repo is at the root of the file system and named 'PowerShell' (example: '$($RootInfo.ValidPath)'), when building and packaging for release!" + $RootInfo['IsValid'] = $false + } + else + { + $RootInfo['IsValid'] = $true + } + + return New-PSOptionsObject ` + -RootInfo ([PSCustomObject]$RootInfo) ` + -Top $Top ` + -Runtime $Runtime ` + -Crossgen $Crossgen.IsPresent ` + -Configuration $Configuration ` + -PSModuleRestore $PSModuleRestore.IsPresent ` + -Framework $Framework ` + -Output $Output ` + -ForMinimalSize $ForMinimalSize +} + +function Start-PSPester { + [CmdletBinding(DefaultParameterSetName='default')] + param( + [Parameter(Position=0)] + [string[]]$Path = @("$PSScriptRoot/test/powershell"), + [string]$OutputFormat = "NUnitXml", + [string]$OutputFile = "pester-tests.xml", + [string[]]$ExcludeTag = 'Slow', + [string[]]$Tag = @("CI","Feature"), + [switch]$ThrowOnFailure, + [string]$BinDir = (Split-Path (Get-PSOptions -DefaultToNew).Output), + [string]$powershell = (Join-Path $BinDir 'pwsh'), + [string]$Pester = ([IO.Path]::Combine($BinDir, "Modules", "Pester")), + [Parameter(ParameterSetName='Unelevate',Mandatory=$true)] + [switch]$Unelevate, + [switch]$Quiet, + [switch]$Terse, + [Parameter(ParameterSetName='PassThru',Mandatory=$true)] + [switch]$PassThru, + [Parameter(ParameterSetName='PassThru',HelpMessage='Run commands on Linux with sudo.')] + [switch]$Sudo, + [switch]$IncludeFailingTest, + [switch]$IncludeCommonTests, + [string]$ExperimentalFeatureName, + [Parameter(HelpMessage='Title to publish the results as.')] + [string]$Title = 'PowerShell 7 Tests', + [Parameter(ParameterSetName='Wait', Mandatory=$true, + HelpMessage='Wait for the debugger to attach to PowerShell before Pester starts. Debug builds only!')] + [switch]$Wait, + [switch]$SkipTestToolBuild + ) + + if (-not (Get-Module -ListAvailable -Name $Pester -ErrorAction SilentlyContinue | Where-Object { $_.Version -ge "4.2" } )) + { + Restore-PSPester + } + + if ($IncludeFailingTest.IsPresent) + { + $Path += "$PSScriptRoot/tools/failingTests" + } + + if($IncludeCommonTests.IsPresent) + { + $path = += "$PSScriptRoot/test/common" + } + + # we need to do few checks and if user didn't provide $ExcludeTag explicitly, we should alternate the default + if ($Unelevate) + { + if (-not $environment.IsWindows) + { + throw '-Unelevate is currently not supported on non-Windows platforms' + } + + if (-not $environment.IsAdmin) + { + throw '-Unelevate cannot be applied because the current user is not Administrator' + } + + if (-not $PSBoundParameters.ContainsKey('ExcludeTag')) + { + $ExcludeTag += 'RequireAdminOnWindows' + } + } + elseif ($environment.IsWindows -and (-not $environment.IsAdmin)) + { + if (-not $PSBoundParameters.ContainsKey('ExcludeTag')) + { + $ExcludeTag += 'RequireAdminOnWindows' + } + } + elseif (-not $environment.IsWindows -and (-not $Sudo.IsPresent)) + { + if (-not $PSBoundParameters.ContainsKey('ExcludeTag')) + { + $ExcludeTag += 'RequireSudoOnUnix' + } + } + elseif (-not $environment.IsWindows -and $Sudo.IsPresent) + { + if (-not $PSBoundParameters.ContainsKey('Tag')) + { + $Tag = 'RequireSudoOnUnix' + } + } + + Write-Verbose "Running pester tests at '$path' with tag '$($Tag -join ''', ''')' and ExcludeTag '$($ExcludeTag -join ''', ''')'" -Verbose + if(!$SkipTestToolBuild.IsPresent) + { + $publishArgs = @{ } + # if we are building for Alpine, we must include the runtime as linux-x64 + # will not build runnable test tools + if ( $environment.IsLinux -and $environment.IsAlpine ) { + $publishArgs['runtime'] = 'alpine-x64' + } + Publish-PSTestTools @publishArgs | ForEach-Object {Write-Host $_} + } + + # All concatenated commands/arguments are suffixed with the delimiter (space) + + # Disable telemetry for all startups of pwsh in tests + $command = "`$env:POWERSHELL_TELEMETRY_OPTOUT = 'yes';" + if ($Terse) + { + $command += "`$ProgressPreference = 'silentlyContinue'; " + } + + # Autoload (in subprocess) temporary modules used in our tests + $newPathFragment = $TestModulePath + $TestModulePathSeparator + $command += '$env:PSModulePath = '+"'$newPathFragment'" + '+$env:PSModulePath;' + + # Windows needs the execution policy adjusted + if ($environment.IsWindows) { + $command += "Set-ExecutionPolicy -Scope Process Unrestricted; " + } + + $command += "Import-Module '$Pester'; " + + if ($Unelevate) + { + if ($environment.IsWindows) { + $outputBufferFilePath = [System.IO.Path]::GetTempFileName() + } + else { + # Azure DevOps agents do not have Temp folder setup on Ubuntu 20.04, hence using HOME directory + $outputBufferFilePath = (Join-Path $env:HOME $([System.IO.Path]::GetRandomFileName())) + } + } + + $command += "Invoke-Pester " + + $command += "-OutputFormat ${OutputFormat} -OutputFile ${OutputFile} " + if ($ExcludeTag -and ($ExcludeTag -ne "")) { + $command += "-ExcludeTag @('" + (${ExcludeTag} -join "','") + "') " + } + if ($Tag) { + $command += "-Tag @('" + (${Tag} -join "','") + "') " + } + # sometimes we need to eliminate Pester output, especially when we're + # doing a daily build as the log file is too large + if ( $Quiet ) { + $command += "-Quiet " + } + if ( $PassThru ) { + $command += "-PassThru " + } + + $command += "'" + ($Path -join "','") + "'" + if ($Unelevate) + { + $command += " *> $outputBufferFilePath; '__UNELEVATED_TESTS_THE_END__' >> $outputBufferFilePath" + } + + Write-Verbose $command + + $script:nonewline = $true + $script:inerror = $false + function Write-Terse([string] $line) + { + $trimmedline = $line.Trim() + if ($trimmedline.StartsWith("[+]")) { + Write-Host "+" -NoNewline -ForegroundColor Green + $script:nonewline = $true + $script:inerror = $false + } + elseif ($trimmedline.StartsWith("[?]")) { + Write-Host "?" -NoNewline -ForegroundColor Cyan + $script:nonewline = $true + $script:inerror = $false + } + elseif ($trimmedline.StartsWith("[!]")) { + Write-Host "!" -NoNewline -ForegroundColor Gray + $script:nonewline = $true + $script:inerror = $false + } + elseif ($trimmedline.StartsWith("Executing script ")) { + # Skip lines where Pester reports that is executing a test script + return + } + elseif ($trimmedline -match "^\d+(\.\d+)?m?s$") { + # Skip the time elapse like '12ms', '1ms', '1.2s' and '12.53s' + return + } + else { + if ($script:nonewline) { + Write-Host "`n" -NoNewline + } + if ($trimmedline.StartsWith("[-]") -or $script:inerror) { + Write-Host $line -ForegroundColor Red + $script:inerror = $true + } + elseif ($trimmedline.StartsWith("VERBOSE:")) { + Write-Host $line -ForegroundColor Yellow + $script:inerror = $false + } + elseif ($trimmedline.StartsWith("Describing") -or $trimmedline.StartsWith("Context")) { + Write-Host $line -ForegroundColor Magenta + $script:inerror = $false + } + else { + Write-Host $line -ForegroundColor Gray + } + $script:nonewline = $false + } + } + + $PSFlags = @("-noprofile") + if (-not [string]::IsNullOrEmpty($ExperimentalFeatureName)) { + + if ($environment.IsWindows) { + $configFile = [System.IO.Path]::GetTempFileName() + } + else { + $configFile = (Join-Path $env:HOME $([System.IO.Path]::GetRandomFileName())) + } + + $configFile = [System.IO.Path]::ChangeExtension($configFile, ".json") + + ## Create the config.json file to enable the given experimental feature. + ## On Windows, we need to have 'RemoteSigned' declared for ExecutionPolicy because the ExecutionPolicy is 'Restricted' by default. + ## On Unix, ExecutionPolicy is not supported, so we don't need to declare it. + if ($environment.IsWindows) { + $content = @" +{ + "Microsoft.PowerShell:ExecutionPolicy":"RemoteSigned", + "ExperimentalFeatures": [ + "$ExperimentalFeatureName" + ] +} +"@ + } else { + $content = @" +{ + "ExperimentalFeatures": [ + "$ExperimentalFeatureName" + ] +} +"@ + } + + Set-Content -Path $configFile -Value $content -Encoding Ascii -Force + $PSFlags = @("-settings", $configFile, "-noprofile") + } + + # -Wait is only available on Debug builds + # It is used to allow the debugger to attach before PowerShell + # runs pester in this case + if($Wait.IsPresent){ + $PSFlags += '-wait' + } + + # To ensure proper testing, the module path must not be inherited by the spawned process + try { + $originalModulePath = $env:PSModulePath + $originalTelemetry = $env:POWERSHELL_TELEMETRY_OPTOUT + $env:POWERSHELL_TELEMETRY_OPTOUT = 'yes' + if ($Unelevate) + { + Start-UnelevatedProcess -process $powershell -arguments ($PSFlags + "-c $Command") + $currentLines = 0 + while ($true) + { + $lines = Get-Content $outputBufferFilePath | Select-Object -Skip $currentLines + if ($Terse) + { + foreach ($line in $lines) + { + Write-Terse -line $line + } + } + else + { + $lines | Write-Host + } + if ($lines | Where-Object { $_ -eq '__UNELEVATED_TESTS_THE_END__'}) + { + break + } + + $count = ($lines | Measure-Object).Count + if ($count -eq 0) + { + Start-Sleep -Seconds 1 + } + else + { + $currentLines += $count + } + } + } + else + { + if ($PassThru.IsPresent) + { + if ($environment.IsWindows) { + $passThruFile = [System.IO.Path]::GetTempFileName() + } + else { + $passThruFile = Join-Path $env:HOME $([System.IO.Path]::GetRandomFileName()) + } + + try + { + $command += "| Export-Clixml -Path '$passThruFile' -Force" + + $passThruCommand = { & $powershell $PSFlags -c $command } + if ($Sudo.IsPresent) { + # -E says to preserve the environment + $passThruCommand = { & sudo -E $powershell $PSFlags -c $command } + } + + $writeCommand = { Write-Host $_ } + if ($Terse) + { + $writeCommand = { Write-Terse $_ } + } + + Start-NativeExecution -sb $passThruCommand | ForEach-Object $writeCommand + Import-Clixml -Path $passThruFile | Where-Object {$_.TotalCount -is [Int32]} + } + finally + { + Remove-Item $passThruFile -ErrorAction SilentlyContinue -Force + } + } + else + { + if ($Terse) + { + Start-NativeExecution -sb {& $powershell $PSFlags -c $command} | ForEach-Object { Write-Terse -line $_ } + } + else + { + Start-NativeExecution -sb {& $powershell $PSFlags -c $command} + } + } + } + } finally { + $env:PSModulePath = $originalModulePath + $env:POWERSHELL_TELEMETRY_OPTOUT = $originalTelemetry + if ($Unelevate) + { + Remove-Item $outputBufferFilePath + } + } + + Publish-TestResults -Path $OutputFile -Title $Title + + if($ThrowOnFailure) + { + Test-PSPesterResults -TestResultsFile $OutputFile + } +} + +function Install-Dotnet { + [CmdletBinding()] + param( + [string]$Channel = $dotnetCLIChannel, + [string]$Version = $dotnetCLIRequiredVersion, + [string]$Quality = $dotnetCLIQuality, + [switch]$NoSudo, + [string]$InstallDir, + [string]$AzureFeed, + [string]$FeedCredential + ) + + # This allows sudo install to be optional; needed when running in containers / as root + # Note that when it is null, Invoke-Expression (but not &) must be used to interpolate properly + $sudo = if (!$NoSudo) { "sudo" } + + $installObtainUrl = "https://dotnet.microsoft.com/download/dotnet-core/scripts/v1" + $uninstallObtainUrl = "https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain" + + # Install for Linux and OS X + if ($environment.IsLinux -or $environment.IsMacOS) { + $wget = Get-Command -Name wget -CommandType Application -TotalCount 1 -ErrorAction Stop + + # Uninstall all previous dotnet packages + $uninstallScript = if ($environment.IsLinux -and $environment.IsUbuntu) { + "dotnet-uninstall-debian-packages.sh" + } elseif ($environment.IsMacOS) { + "dotnet-uninstall-pkgs.sh" + } + + if ($uninstallScript) { + Start-NativeExecution { + & $wget $uninstallObtainUrl/uninstall/$uninstallScript + Invoke-Expression "$sudo bash ./$uninstallScript" + } + } else { + Write-Warning "This script only removes prior versions of dotnet for Ubuntu and OS X" + } + + # Install new dotnet 1.1.0 preview packages + $installScript = "dotnet-install.sh" + Start-NativeExecution { + Write-Verbose -Message "downloading install script from $installObtainUrl/$installScript ..." -Verbose + & $wget $installObtainUrl/$installScript + + if ((Get-ChildItem "./$installScript").Length -eq 0) { + throw "./$installScript was 0 length" + } + + if ($Version) { + $bashArgs = @("./$installScript", '-v', $Version, '-q', $Quality) + } + elseif ($Channel) { + $bashArgs = @("./$installScript", '-c', $Channel, '-q', $Quality) + } + + if ($InstallDir) { + $bashArgs += @('-i', $InstallDir) + } + + if ($AzureFeed) { + $bashArgs += @('-AzureFeed', $AzureFeed, '-FeedCredential', $FeedCredential) + } + + bash @bashArgs + } + } elseif ($environment.IsWindows) { + Remove-Item -ErrorAction SilentlyContinue -Recurse -Force ~\AppData\Local\Microsoft\dotnet + $installScript = "dotnet-install.ps1" + Invoke-WebRequest -Uri $installObtainUrl/$installScript -OutFile $installScript + if (-not $environment.IsCoreCLR) { + $installArgs = @{ + Quality = $Quality + } + + if ($Version) { + $installArgs += @{ Version = $Version } + } elseif ($Channel) { + $installArgs += @{ Channel = $Channel } + } + + if ($InstallDir) { + $installArgs += @{ InstallDir = $InstallDir } + } + + if ($AzureFeed) { + $installArgs += @{ + AzureFeed = $AzureFeed + $FeedCredential = $FeedCredential + } + } + + & ./$installScript @installArgs + } + else { + # dotnet-install.ps1 uses APIs that are not supported in .NET Core, so we run it with Windows PowerShell + $fullPSPath = Join-Path -Path $env:windir -ChildPath "System32\WindowsPowerShell\v1.0\powershell.exe" + $fullDotnetInstallPath = Join-Path -Path $PWD.Path -ChildPath $installScript + Start-NativeExecution { + + if ($Version) { + $psArgs = @('-NoLogo', '-NoProfile', '-File', $fullDotnetInstallPath, '-Version', $Version, '-Quality', $Quality) + } + elseif ($Channel) { + $psArgs = @('-NoLogo', '-NoProfile', '-File', $fullDotnetInstallPath, '-Channel', $Channel, '-Quality', $Quality) + } + + if ($InstallDir) { + $psArgs += @('-InstallDir', $InstallDir) + } + + if ($AzureFeed) { + $psArgs += @('-AzureFeed', $AzureFeed, '-FeedCredential', $FeedCredential) + } + + & $fullPSPath @psArgs + } + } + } +} + +function Start-PSBootstrap { + [CmdletBinding()] + param( + [string]$Channel = $dotnetCLIChannel, + # we currently pin dotnet-cli version, and will + # update it when more stable version comes out. + [string]$Version = $dotnetCLIRequiredVersion, + [switch]$Package, + [switch]$NoSudo, + [switch]$BuildLinuxArm, + [switch]$Force + ) + + Write-Log -message "Installing PowerShell build dependencies" + + Push-Location $PSScriptRoot/tools + + try { + if ($environment.IsLinux -or $environment.IsMacOS) { + # This allows sudo install to be optional; needed when running in containers / as root + # Note that when it is null, Invoke-Expression (but not &) must be used to interpolate properly + $sudo = if (!$NoSudo) { "sudo" } + + if ($BuildLinuxArm -and $environment.IsLinux -and -not $environment.IsUbuntu) { + Write-Error "Cross compiling for linux-arm is only supported on Ubuntu environment" + return + } + + # Install ours and .NET's dependencies + $Deps = @() + if ($environment.IsLinux -and $environment.IsUbuntu) { + # Build tools + $Deps += "curl", "g++", "make" + + if ($BuildLinuxArm) { + $Deps += "gcc-arm-linux-gnueabihf", "g++-arm-linux-gnueabihf" + } + + # .NET Core required runtime libraries + $Deps += "libunwind8" + if ($environment.IsUbuntu16) { $Deps += "libicu55" } + elseif ($environment.IsUbuntu18) { $Deps += "libicu60"} + + # Packaging tools + if ($Package) { $Deps += "ruby-dev", "groff", "libffi-dev" } + + # Install dependencies + # change the fontend from apt-get to noninteractive + $originalDebianFrontEnd=$env:DEBIAN_FRONTEND + $env:DEBIAN_FRONTEND='noninteractive' + try { + Start-NativeExecution { + Invoke-Expression "$sudo apt-get update -qq" + Invoke-Expression "$sudo apt-get install -y -qq $Deps" + } + } + finally { + # change the apt frontend back to the original + $env:DEBIAN_FRONTEND=$originalDebianFrontEnd + } + } elseif ($environment.IsLinux -and $environment.IsRedHatFamily) { + # Build tools + $Deps += "which", "curl", "gcc-c++", "make" + + # .NET Core required runtime libraries + $Deps += "libicu", "libunwind" + + # Packaging tools + if ($Package) { $Deps += "ruby-devel", "rpm-build", "groff", 'libffi-devel' } + + $PackageManager = Get-RedHatPackageManager + + $baseCommand = "$sudo $PackageManager" + + # On OpenSUSE 13.2 container, sudo does not exist, so don't use it if not needed + if($NoSudo) + { + $baseCommand = $PackageManager + } + + # Install dependencies + Start-NativeExecution { + Invoke-Expression "$baseCommand $Deps" + } + } elseif ($environment.IsLinux -and $environment.IsSUSEFamily) { + # Build tools + $Deps += "gcc", "make" + + # Packaging tools + if ($Package) { $Deps += "ruby-devel", "rpmbuild", "groff", 'libffi-devel' } + + $PackageManager = "zypper --non-interactive install" + $baseCommand = "$sudo $PackageManager" + + # On OpenSUSE 13.2 container, sudo does not exist, so don't use it if not needed + if($NoSudo) + { + $baseCommand = $PackageManager + } + + # Install dependencies + Start-NativeExecution { + Invoke-Expression "$baseCommand $Deps" + } + } elseif ($environment.IsMacOS) { + if ($environment.UsingHomebrew) { + $PackageManager = "brew" + } elseif ($environment.UsingMacports) { + $PackageManager = "$sudo port" + } + + # .NET Core required runtime libraries + $Deps += "openssl" + + # Install dependencies + # ignore exitcode, because they may be already installed + Start-NativeExecution ([ScriptBlock]::Create("$PackageManager install $Deps")) -IgnoreExitcode + } elseif ($environment.IsLinux -and $environment.IsAlpine) { + $Deps += 'libunwind', 'libcurl', 'bash', 'clang', 'build-base', 'git', 'curl' + + Start-NativeExecution { + Invoke-Expression "apk add $Deps" + } + } + + # Install [fpm](https://github.com/jordansissel/fpm) and [ronn](https://github.com/rtomayko/ronn) + if ($Package) { + try { + # We cannot guess if the user wants to run gem install as root on linux and windows, + # but macOs usually requires sudo + $gemsudo = '' + if($environment.IsMacOS -or $env:TF_BUILD) { + $gemsudo = $sudo + } + Start-NativeExecution ([ScriptBlock]::Create("$gemsudo gem install ffi -v 1.12.0 --no-document")) + Start-NativeExecution ([ScriptBlock]::Create("$gemsudo gem install fpm -v 1.11.0 --no-document")) + Start-NativeExecution ([ScriptBlock]::Create("$gemsudo gem install ronn -v 0.7.3 --no-document")) + } catch { + Write-Warning "Installation of fpm and ronn gems failed! Must resolve manually." + } + } + } + + # Try to locate dotnet-SDK before installing it + Find-Dotnet + + # Install dotnet-SDK + $dotNetExists = precheck 'dotnet' $null + $dotNetVersion = [string]::Empty + if($dotNetExists) { + $dotNetVersion = Start-NativeExecution -sb { dotnet --version } -IgnoreExitcode + } + + if(!$dotNetExists -or $dotNetVersion -ne $dotnetCLIRequiredVersion -or $Force.IsPresent) { + if($Force.IsPresent) { + Write-Log -message "Installing dotnet due to -Force." + } + elseif(!$dotNetExists) { + Write-Log -message "dotnet not present. Installing dotnet." + } + else { + Write-Log -message "dotnet out of date ($dotNetVersion). Updating dotnet." + } + + $DotnetArguments = @{ Channel=$Channel; Version=$Version; NoSudo=$NoSudo } + Install-Dotnet @DotnetArguments + } + else { + Write-Log -message "dotnet is already installed. Skipping installation." + } + + # Install Windows dependencies if `-Package` or `-BuildWindowsNative` is specified + if ($environment.IsWindows) { + ## The VSCode build task requires 'pwsh.exe' to be found in Path + if (-not (Get-Command -Name pwsh.exe -CommandType Application -ErrorAction Ignore)) + { + Write-Log -message "pwsh.exe not found. Install latest PowerShell release and add it to Path" + $psInstallFile = [System.IO.Path]::Combine($PSScriptRoot, "tools", "install-powershell.ps1") + & $psInstallFile -AddToPath + } + } + } finally { + Pop-Location + } +} + +function Start-CrossGen { + [CmdletBinding()] + param( + [Parameter(Mandatory= $true)] + [ValidateNotNullOrEmpty()] + [String] + $PublishPath, + + [Parameter(Mandatory=$true)] + [ValidateSet("alpine-x64", + "linux-arm", + "linux-arm64", + "linux-x64", + "osx-arm64", + "osx-x64", + "win-arm", + "win-arm64", + "win7-x64", + "win7-x86")] + [string] + $Runtime + ) + + function New-CrossGenAssembly { + param ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String[]] + $AssemblyPath, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [String] + $CrossgenPath, + + [Parameter(Mandatory = $true)] + [ValidateSet("alpine-x64", + "linux-arm", + "linux-arm64", + "linux-x64", + "osx-arm64", + "osx-x64", + "win-arm", + "win-arm64", + "win7-x64", + "win7-x86")] + [string] + $Runtime + ) + + $platformAssembliesPath = Split-Path $AssemblyPath[0] -Parent + + $targetOS, $targetArch = $Runtime -split '-' + + # Special cases where OS / Arch does not conform with runtime names + switch ($Runtime) { + 'alpine-x64' { + $targetOS = 'linux' + $targetArch = 'x64' + } + 'win-arm' { + $targetOS = 'windows' + $targetArch = 'arm' + } + 'win-arm64' { + $targetOS = 'windows' + $targetArch = 'arm64' + } + 'win7-x64' { + $targetOS = 'windows' + $targetArch = 'x64' + } + 'win7-x86' { + $targetOS = 'windows' + $targetArch = 'x86' + } + } + + $generatePdb = $targetos -eq 'windows' + + # The path to folder must end with directory separator + $dirSep = [System.IO.Path]::DirectorySeparatorChar + $platformAssembliesPath = if (-not $platformAssembliesPath.EndsWith($dirSep)) { $platformAssembliesPath + $dirSep } + + Start-NativeExecution { + $crossgen2Params = @( + "-r" + $platformAssembliesPath + "--out-near-input" + "--single-file-compilation" + "-O" + "--targetos" + $targetOS + "--targetarch" + $targetArch + ) + + if ($generatePdb) { + $crossgen2Params += "--pdb" + } + + $crossgen2Params += $AssemblyPath + + & $CrossgenPath $crossgen2Params + } + } + + if (-not (Test-Path $PublishPath)) { + throw "Path '$PublishPath' does not exist." + } + + # Get the path to crossgen + $crossGenExe = if ($environment.IsWindows) { "crossgen2.exe" } else { "crossgen2" } + + # The crossgen tool is only published for these particular runtimes + $crossGenRuntime = if ($environment.IsWindows) { + # for windows the tool architecture is the host machine architecture, so it is always x64. + # we can cross compile for x86, arm and arm64 + "win-x64" + } else { + $Runtime + } + + if (-not $crossGenRuntime) { + throw "crossgen is not available for this platform" + } + + $dotnetRuntimeVersion = $script:Options.Framework -replace 'net' + + # Get the CrossGen.exe for the correct runtime with the latest version + $crossGenPath = Get-ChildItem $script:Environment.nugetPackagesRoot $crossGenExe -Recurse | ` + Where-Object { $_.FullName -match $crossGenRuntime } | ` + Where-Object { $_.FullName -match $dotnetRuntimeVersion } | ` + Where-Object { (Split-Path $_.FullName -Parent).EndsWith('tools') } | ` + Sort-Object -Property FullName -Descending | ` + Select-Object -First 1 | ` + ForEach-Object { $_.FullName } + if (-not $crossGenPath) { + throw "Unable to find latest version of crossgen2.exe. 'Please run Start-PSBuild -Clean' first, and then try again." + } + Write-Verbose "Matched CrossGen2.exe: $crossGenPath" -Verbose + + # Common assemblies used by Add-Type or assemblies with high JIT and no pdbs to crossgen + $commonAssembliesForAddType = @( + "Microsoft.CodeAnalysis.CSharp.dll" + "Microsoft.CodeAnalysis.dll" + "System.Linq.Expressions.dll" + "Microsoft.CSharp.dll" + "System.Runtime.Extensions.dll" + "System.Linq.dll" + "System.Collections.Concurrent.dll" + "System.Collections.dll" + "Newtonsoft.Json.dll" + "System.IO.FileSystem.dll" + "System.Diagnostics.Process.dll" + "System.Threading.Tasks.Parallel.dll" + "System.Security.AccessControl.dll" + "System.Text.Encoding.CodePages.dll" + "System.Private.Uri.dll" + "System.Threading.dll" + "System.Security.Principal.Windows.dll" + "System.Console.dll" + "Microsoft.Win32.Registry.dll" + "System.IO.Pipes.dll" + "System.Diagnostics.FileVersionInfo.dll" + "System.Collections.Specialized.dll" + "Microsoft.ApplicationInsights.dll" + ) + + $fullAssemblyList = $commonAssembliesForAddType + + $assemblyFullPaths = @() + $assemblyFullPaths += foreach ($assemblyName in $fullAssemblyList) { + Join-Path $PublishPath $assemblyName + } + + New-CrossGenAssembly -CrossgenPath $crossGenPath -AssemblyPath $assemblyFullPaths -Runtime $Runtime + + # + # With the latest dotnet.exe, the default load context is only able to load TPAs, and TPA + # only contains IL assembly names. In order to make the default load context able to load + # the NI PS assemblies, we need to replace the IL PS assemblies with the corresponding NI + # PS assemblies, but with the same IL assembly names. + # + Write-Verbose "PowerShell Ngen assemblies have been generated. Deploying ..." -Verbose + foreach ($assemblyName in $fullAssemblyList) { + + # Remove the IL assembly and its symbols. + $assemblyPath = Join-Path $PublishPath $assemblyName + $symbolsPath = [System.IO.Path]::ChangeExtension($assemblyPath, ".pdb") + + Remove-Item $assemblyPath -Force -ErrorAction Stop + + # Rename the corresponding ni.dll assembly to be the same as the IL assembly + $niAssemblyPath = [System.IO.Path]::ChangeExtension($assemblyPath, "ni.dll") + Rename-Item $niAssemblyPath $assemblyPath -Force -ErrorAction Stop + + # No symbols are available for Microsoft.CodeAnalysis.CSharp.dll, Microsoft.CodeAnalysis.dll, + # Microsoft.CodeAnalysis.VisualBasic.dll, and Microsoft.CSharp.dll. + if ($commonAssembliesForAddType -notcontains $assemblyName) { + Remove-Item $symbolsPath -Force -ErrorAction Stop + } + } +} + +function Use-PSClass { + [CmdletBinding()] + param ( + [Parameter(ValueFromPipeline = $true, Mandatory = $true, Position = 0)] + [string[]]$Logfile, + [Parameter()][switch]$IncludeEmpty, + [Parameter()][switch]$MultipleLog + ) + <# +Convert our test logs to +xunit schema - top level assemblies +Pester conversion +foreach $r in "test-results"."test-suite".results."test-suite" +assembly + name = $r.Description + config-file = log file (this is the only way we can determine between admin/nonadmin log) + test-framework = Pester + environment = top-level "test-results.environment.platform + run-date = date (doesn't exist in pester except for beginning) + run-time = time + time = +#> + + BEGIN { + # CLASSES + class assemblies { + # attributes + [datetime]$timestamp + # child elements + [System.Collections.Generic.List[testAssembly]]$assembly + assemblies() { + $this.timestamp = [datetime]::now + $this.assembly = [System.Collections.Generic.List[testAssembly]]::new() + } + static [assemblies] op_Addition([assemblies]$ls, [assemblies]$rs) { + $newAssembly = [assemblies]::new() + $newAssembly.assembly.AddRange($ls.assembly) + $newAssembly.assembly.AddRange($rs.assembly) + return $newAssembly + } + [string]ToString() { + $sb = [text.stringbuilder]::new() + $sb.AppendLine('' -f $this.timestamp) + foreach ( $a in $this.assembly ) { + $sb.Append("$a") + } + $sb.AppendLine(""); + return $sb.ToString() + } + # use Write-Output to emit these into the pipeline + [array]GetTests() { + return $this.Assembly.collection.test + } + } + + class testAssembly { + # attributes + [string]$name # path to pester file + [string]${config-file} + [string]${test-framework} # Pester + [string]$environment + [string]${run-date} + [string]${run-time} + [decimal]$time + [int]$total + [int]$passed + [int]$failed + [int]$skipped + [int]$errors + testAssembly ( ) { + $this."config-file" = "no config" + $this."test-framework" = "Pester" + $this.environment = $script:environment + $this."run-date" = $script:rundate + $this."run-time" = $script:runtime + $this.collection = [System.Collections.Generic.List[collection]]::new() + } + # child elements + [error[]]$error + [System.Collections.Generic.List[collection]]$collection + [string]ToString() { + $sb = [System.Text.StringBuilder]::new() + $sb.AppendFormat(' ") + if ( $this.error ) { + $sb.AppendLine(" ") + foreach ( $e in $this.error ) { + $sb.AppendLine($e.ToString()) + } + $sb.AppendLine(" ") + } else { + $sb.AppendLine(" ") + } + foreach ( $col in $this.collection ) { + $sb.AppendLine($col.ToString()) + } + $sb.AppendLine(" ") + return $sb.ToString() + } + } + + class collection { + # attributes + [string]$name + [decimal]$time + [int]$total + [int]$passed + [int]$failed + [int]$skipped + # child element + [System.Collections.Generic.List[test]]$test + # constructor + collection () { + $this.test = [System.Collections.Generic.List[test]]::new() + } + [string]ToString() { + $sb = [Text.StringBuilder]::new() + if ( $this.test.count -eq 0 ) { + $sb.AppendLine(" ") + } else { + $sb.AppendFormat(' ' + "`n", + $this.total, $this.passed, $this.failed, $this.skipped, [security.securityelement]::escape($this.name), $this.time) + foreach ( $t in $this.test ) { + $sb.AppendLine(" " + $t.ToString()); + } + $sb.Append(" ") + } + return $sb.ToString() + } + } + + class errors { + [error[]]$error + } + class error { + # attributes + [string]$type + [string]$name + # child elements + [failure]$failure + [string]ToString() { + $sb = [system.text.stringbuilder]::new() + $sb.AppendLine('' -f $this.type, [security.securityelement]::escape($this.Name)) + $sb.AppendLine($this.failure -as [string]) + $sb.AppendLine("") + return $sb.ToString() + } + } + + class cdata { + [string]$text + cdata ( [string]$s ) { $this.text = $s } + [string]ToString() { + return '' + } + } + + class failure { + [string]${exception-type} + [cdata]$message + [cdata]${stack-trace} + failure ( [string]$message, [string]$stack ) { + $this."exception-type" = "Pester" + $this.Message = [cdata]::new($message) + $this."stack-trace" = [cdata]::new($stack) + } + [string]ToString() { + $sb = [text.stringbuilder]::new() + $sb.AppendLine(" ") + $sb.AppendLine(" " + ($this.message -as [string]) + "") + $sb.AppendLine(" " + ($this."stack-trace" -as [string]) + "") + $sb.Append(" ") + return $sb.ToString() + } + } + + enum resultenum { + Pass + Fail + Skip + } + + class trait { + # attributes + [string]$name + [string]$value + } + class traits { + [trait[]]$trait + } + class test { + # attributes + [string]$name + [string]$type + [string]$method + [decimal]$time + [resultenum]$result + # child elements + [trait[]]$traits + [failure]$failure + [cdata]$reason # skip reason + [string]ToString() { + $sb = [text.stringbuilder]::new() + $sb.appendformat(' ") + $sb.AppendLine($this.failure -as [string]) + $sb.append(' ') + } else { + $sb.Append("/>") + } + return $sb.ToString() + } + } + + function convert-pesterlog ( [xml]$x, $logpath, [switch]$includeEmpty ) { + <#$resultMap = @{ + Success = "Pass" + Ignored = "Skip" + Failure = "Fail" + }#> + + $resultMap = @{ + Success = "Pass" + Ignored = "Skip" + Failure = "Fail" + Inconclusive = "Skip" + } + + $configfile = $logpath + $runtime = $x."test-results".time + $environment = $x."test-results".environment.platform + "-" + $x."test-results".environment."os-version" + $rundate = $x."test-results".date + $suites = $x."test-results"."test-suite".results."test-suite" + $assemblies = [assemblies]::new() + foreach ( $suite in $suites ) { + $tCases = $suite.SelectNodes(".//test-case") + # only create an assembly group if we have tests + if ( $tCases.count -eq 0 -and ! $includeEmpty ) { continue } + $tGroup = $tCases | Group-Object result + $total = $tCases.Count + $asm = [testassembly]::new() + $asm.environment = $environment + $asm."run-date" = $rundate + $asm."run-time" = $runtime + $asm.Name = $suite.name + $asm."config-file" = $configfile + $asm.time = $suite.time + $asm.total = $suite.SelectNodes(".//test-case").Count + $asm.Passed = $tGroup| Where-Object -FilterScript {$_.Name -eq "Success"} | ForEach-Object -Process {$_.Count} + $asm.Failed = $tGroup| Where-Object -FilterScript {$_.Name -eq "Failure"} | ForEach-Object -Process {$_.Count} + $asm.Skipped = $tGroup| Where-Object -FilterScript { $_.Name -eq "Ignored" } | ForEach-Object -Process {$_.Count} + $asm.Skipped += $tGroup| Where-Object -FilterScript { $_.Name -eq "Inconclusive" } | ForEach-Object -Process {$_.Count} + $c = [collection]::new() + $c.passed = $asm.Passed + $c.failed = $asm.failed + $c.skipped = $asm.skipped + $c.total = $asm.total + $c.time = $asm.time + $c.name = $asm.name + foreach ( $tc in $suite.SelectNodes(".//test-case")) { + if ( $tc.result -match "Success|Ignored|Failure" ) { + $t = [test]::new() + $t.name = $tc.Name + $t.time = $tc.time + $t.method = $tc.description # the pester actually puts the name of the "it" as description + $t.type = $suite.results."test-suite".description | Select-Object -First 1 + $t.result = $resultMap[$tc.result] + if ( $tc.failure ) { + $t.failure = [failure]::new($tc.failure.message, $tc.failure."stack-trace") + } + $null = $c.test.Add($t) + } + } + $null = $asm.collection.add($c) + $assemblies.assembly.Add($asm) + } + $assemblies + } + + # convert it to our object model + # a simple conversion + function convert-xunitlog { + param ( $x, $logpath ) + $asms = [assemblies]::new() + $asms.timestamp = $x.assemblies.timestamp + foreach ( $assembly in $x.assemblies.assembly ) { + $asm = [testAssembly]::new() + $asm.environment = $assembly.environment + $asm."test-framework" = $assembly."test-framework" + $asm."run-date" = $assembly."run-date" + $asm."run-time" = $assembly."run-time" + $asm.total = $assembly.total + $asm.passed = $assembly.passed + $asm.failed = $assembly.failed + $asm.skipped = $assembly.skipped + $asm.time = $assembly.time + $asm.name = $assembly.name + foreach ( $coll in $assembly.collection ) { + $c = [collection]::new() + $c.name = $coll.name + $c.total = $coll.total + $c.passed = $coll.passed + $c.failed = $coll.failed + $c.skipped = $coll.skipped + $c.time = $coll.time + foreach ( $t in $coll.test ) { + $test = [test]::new() + $test.name = $t.name + $test.type = $t.type + $test.method = $t.method + $test.time = $t.time + $test.result = $t.result + $c.test.Add($test) + } + $null = $asm.collection.add($c) + } + $null = $asms.assembly.add($asm) + } + $asms + } + $Logs = @() + } + + PROCESS { + #### MAIN #### + foreach ( $log in $Logfile ) { + foreach ( $logpath in (Resolve-Path $log).path ) { + Write-Progress "converting file $logpath" + if ( ! $logpath) { throw "Cannot resolve $Logfile" } + $x = [xml](Get-Content -Raw -ReadCount 0 $logpath) + + if ( $x.psobject.properties['test-results'] ) { + $Logs += convert-pesterlog $x $logpath -includeempty:$includeempty + } elseif ( $x.psobject.properties['assemblies'] ) { + $Logs += convert-xunitlog $x $logpath -includeEmpty:$includeEmpty + } else { + Write-Error "Cannot determine log type" + } + } + } + } + + END { + if ( $MultipleLog ) { + $Logs + } else { + $combinedLog = $Logs[0] + for ( $i = 1; $i -lt $logs.count; $i++ ) { + $combinedLog += $Logs[$i] + } + $combinedLog + } + } +} + +function Start-PSPackage { + [CmdletBinding(DefaultParameterSetName='Version',SupportsShouldProcess=$true)] + param( + # PowerShell packages use Semantic Versioning https://semver.org/ + [Parameter(ParameterSetName = "Version")] + [string]$Version, + + [Parameter(ParameterSetName = "ReleaseTag")] + [ValidatePattern("^v\d+\.\d+\.\d+(-\w+(\.\d{1,2})?)?$")] + [ValidateNotNullOrEmpty()] + [string]$ReleaseTag, + + # Package name + [ValidatePattern("^powershell")] + [string]$Name = "powershell", + + # Ubuntu, CentOS, Fedora, macOS, and Windows packages are supported + [ValidateSet("msix", "deb", "osxpkg", "rpm", "msi", "zip", "zip-pdb", "nupkg", "tar", "tar-arm", "tar-arm64", "tar-alpine", "fxdependent", "fxdependent-win-desktop", "min-size")] + [string[]]$Type, + + # Generate windows downlevel package + [ValidateSet("win7-x86", "win7-x64", "win-arm", "win-arm64")] + [ValidateScript({$Environment.IsWindows})] + [string] $WindowsRuntime, + + [ValidateSet('osx-x64', 'osx-arm64')] + [ValidateScript({$Environment.IsMacOS})] + [string] $MacOSRuntime, + + [Switch] $Force, + + [Switch] $SkipReleaseChecks, + + [switch] $NoSudo, + + [switch] $LTS + ) + + DynamicParam { + if ($Type -in ('zip', 'min-size') -or $Type -like 'fxdependent*') { + # Add a dynamic parameter '-IncludeSymbols' when the specified package type is 'zip' only. + # The '-IncludeSymbols' parameter can be used to indicate that the package should only contain powershell binaries and symbols. + $ParameterAttr = New-Object "System.Management.Automation.ParameterAttribute" + $Attributes = New-Object "System.Collections.ObjectModel.Collection``1[System.Attribute]" + $Attributes.Add($ParameterAttr) > $null + + $Parameter = New-Object "System.Management.Automation.RuntimeDefinedParameter" -ArgumentList ("IncludeSymbols", [switch], $Attributes) + $Dict = New-Object "System.Management.Automation.RuntimeDefinedParameterDictionary" + $Dict.Add("IncludeSymbols", $Parameter) > $null + return $Dict + } + } + + End { + $IncludeSymbols = $null + if ($PSBoundParameters.ContainsKey('IncludeSymbols')) { + Write-Log 'setting IncludeSymbols' + $IncludeSymbols = $PSBoundParameters['IncludeSymbols'] + } + + # Runtime and Configuration settings required by the package + ($Runtime, $Configuration) = if ($WindowsRuntime) { + $WindowsRuntime, "Release" + } elseif ($MacOSRuntime) { + $MacOSRuntime, "Release" + } elseif ($Type -eq "tar-alpine") { + New-PSOptions -Configuration "Release" -Runtime "alpine-x64" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration } + } elseif ($Type -eq "tar-arm") { + New-PSOptions -Configuration "Release" -Runtime "Linux-ARM" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration } + } elseif ($Type -eq "tar-arm64") { + if ($IsMacOS) { + New-PSOptions -Configuration "Release" -Runtime "osx-arm64" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration } + } else { + New-PSOptions -Configuration "Release" -Runtime "Linux-ARM64" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration } + } + } else { + New-PSOptions -Configuration "Release" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration } + } + + if ($Environment.IsWindows) { + # Runtime will be one of win7-x64, win7-x86, "win-arm" and "win-arm64" on Windows. + # Build the name suffix for universal win-plat packages. + switch ($Runtime) { + "win-arm" { $NameSuffix = "win-arm32" } + "win-arm64" { $NameSuffix = "win-arm64" } + default { $NameSuffix = $_ -replace 'win\d+', 'win' } + } + } + + if ($Type -eq 'fxdependent') { + $NameSuffix = "win-fxdependent" + Write-Log "Packaging : '$Type'; Packaging Configuration: '$Configuration'" + } elseif ($Type -eq 'fxdependent-win-desktop') { + $NameSuffix = "win-fxdependentWinDesktop" + Write-Log "Packaging : '$Type'; Packaging Configuration: '$Configuration'" + } elseif ($MacOSRuntime) { + $NameSuffix = $MacOSRuntime + } else { + Write-Log "Packaging RID: '$Runtime'; Packaging Configuration: '$Configuration'" + } + + $Script:Options = Get-PSOptions + $actualParams = @() + + $crossGenCorrect = $false + if ($Runtime -match "arm" -or $Type -eq 'min-size') { + ## crossgen doesn't support arm32/64; + ## For the min-size package, we intentionally avoid crossgen. + $crossGenCorrect = $true + } + elseif ($Script:Options.CrossGen) { + $actualParams += '-CrossGen' + $crossGenCorrect = $true + } + + $PSModuleRestoreCorrect = $false + + # Require PSModuleRestore for packaging without symbols + # But Disallow it when packaging with symbols + if (!$IncludeSymbols.IsPresent -and $Script:Options.PSModuleRestore) { + $actualParams += '-PSModuleRestore' + $PSModuleRestoreCorrect = $true + } + elseif ($IncludeSymbols.IsPresent -and !$Script:Options.PSModuleRestore) { + $PSModuleRestoreCorrect = $true + } + else { + $actualParams += '-PSModuleRestore' + } + + $precheckFailed = if ($Type -like 'fxdependent*' -or $Type -eq 'tar-alpine') { + ## We do not check for runtime and crossgen for framework dependent package. + -not $Script:Options -or ## Start-PSBuild hasn't been executed yet + -not $PSModuleRestoreCorrect -or ## Last build didn't specify '-PSModuleRestore' correctly + $Script:Options.Configuration -ne $Configuration -or ## Last build was with configuration other than 'Release' + $Script:Options.Framework -ne $script:netCoreRuntime ## Last build wasn't for CoreCLR + } else { + -not $Script:Options -or ## Start-PSBuild hasn't been executed yet + -not $crossGenCorrect -or ## Last build didn't specify '-CrossGen' correctly + -not $PSModuleRestoreCorrect -or ## Last build didn't specify '-PSModuleRestore' correctly + $Script:Options.Runtime -ne $Runtime -or ## Last build wasn't for the required RID + $Script:Options.Configuration -ne $Configuration -or ## Last build was with configuration other than 'Release' + $Script:Options.Framework -ne $script:netCoreRuntime ## Last build wasn't for CoreCLR + } + + # Make sure the most recent build satisfies the package requirement + if ($precheckFailed) { + # It's possible that the most recent build doesn't satisfy the package requirement but + # an earlier build does. + # It's also possible that the last build actually satisfies the package requirement but + # then `Start-PSPackage` runs from a new PS session or `build.psm1` was reloaded. + # + # In these cases, the user will be asked to build again even though it's technically not + # necessary. However, we want it that way -- being very explict when generating packages. + # This check serves as a simple gate to ensure that the user knows what he is doing, and + # also ensure `Start-PSPackage` does what the user asks/expects, because once packages + # are generated, it'll be hard to verify if they were built from the correct content. + + + $params = @('-Clean') + + # CrossGen cannot be done for framework dependent package as it is runtime agnostic. + if ($Type -notlike 'fxdependent*') { + $params += '-CrossGen' + } + + if (!$IncludeSymbols.IsPresent) { + $params += '-PSModuleRestore' + } + + $actualParams += '-Runtime ' + $Script:Options.Runtime + + if ($Type -eq 'fxdependent') { + $params += '-Runtime', 'fxdependent' + } elseif ($Type -eq 'fxdependent-win-desktop') { + $params += '-Runtime', 'fxdependent-win-desktop' + } else { + $params += '-Runtime', $Runtime + } + + $params += '-Configuration', $Configuration + $actualParams += '-Configuration ' + $Script:Options.Configuration + + Write-Warning "Build started with unexpected parameters 'Start-PSBuild $actualParams" + throw "Please ensure you have run 'Start-PSBuild $params'!" + } + + if ($SkipReleaseChecks.IsPresent) { + Write-Warning "Skipping release checks." + } + elseif (!$Script:Options.RootInfo.IsValid){ + throw $Script:Options.RootInfo.Warning + } + + # If ReleaseTag is specified, use the given tag to calculate Version + if ($PSCmdlet.ParameterSetName -eq "ReleaseTag") { + $Version = $ReleaseTag -Replace '^v' + } + + # Use Git tag if not given a version + if (-not $Version) { + $Version = (git --git-dir="$RepoRoot/.git" describe) -Replace '^v' + } + + $Source = Split-Path -Path $Script:Options.Output -Parent + + # Copy the ThirdPartyNotices.txt so it's part of the package + Copy-Item "$RepoRoot/ThirdPartyNotices.txt" -Destination $Source -Force + + # Copy the default.help.txt so it's part of the package + Copy-Item "$RepoRoot/assets/default.help.txt" -Destination "$Source/en-US" -Force + + # If building a symbols package, we add a zip of the parent to publish + if ($IncludeSymbols.IsPresent) + { + $publishSource = $Source + $buildSource = Split-Path -Path $Source -Parent + $Source = New-TempFolder + $symbolsSource = New-TempFolder + + try + { + # Copy files which go into the root package + Get-ChildItem -Path $publishSource | Copy-Item -Destination $Source -Recurse + + $signingXml = [xml] (Get-Content (Join-Path $PSScriptRoot "..\releaseBuild\signing.xml" -Resolve)) + # Only include the files we sign for compliance scanning, those are the files we build. + $filesToInclude = $signingXml.SignConfigXML.job.file.src | Where-Object { -not $_.endswith('pwsh.exe') -and ($_.endswith(".dll") -or $_.endswith(".exe")) } | ForEach-Object { ($_ -split '\\')[-1] } + $filesToInclude += $filesToInclude | ForEach-Object { $_ -replace '.dll', '.pdb' } + Get-ChildItem -Path $buildSource | Where-Object { $_.Name -in $filesToInclude } | Copy-Item -Destination $symbolsSource -Recurse + + # Zip symbols.zip to the root package + $zipSource = Join-Path $symbolsSource -ChildPath '*' + $zipPath = Join-Path -Path $Source -ChildPath 'symbols.zip' + Save-PSOptions -PSOptionsPath (Join-Path -Path $source -ChildPath 'psoptions.json') -Options $Script:Options + Compress-Archive -Path $zipSource -DestinationPath $zipPath + } + finally + { + Remove-Item -Path $symbolsSource -Recurse -Force -ErrorAction SilentlyContinue + } + } + + Write-Log "Packaging Source: '$Source'" + + # Decide package output type + if (-not $Type) { + $Type = if ($Environment.IsLinux) { + if ($Environment.LinuxInfo.ID -match "ubuntu") { + "deb", "nupkg", "tar" + } elseif ($Environment.IsRedHatFamily) { + "rpm", "nupkg" + } elseif ($Environment.IsSUSEFamily) { + "rpm", "nupkg" + } else { + throw "Building packages for $($Environment.LinuxInfo.PRETTY_NAME) is unsupported!" + } + } elseif ($Environment.IsMacOS) { + "osxpkg", "nupkg", "tar" + } elseif ($Environment.IsWindows) { + "msi", "nupkg", "msix" + } + Write-Warning "-Type was not specified, continuing with $Type!" + } + Write-Log "Packaging Type: $Type" + + # Add the symbols to the suffix + # if symbols are specified to be included + if ($IncludeSymbols.IsPresent -and $NameSuffix) { + $NameSuffix = "symbols-$NameSuffix" + } + elseif ($IncludeSymbols.IsPresent) { + $NameSuffix = "symbols" + } + + switch ($Type) { + "zip" { + $Arguments = @{ + PackageNameSuffix = $NameSuffix + PackageSourcePath = $Source + PackageVersion = $Version + Force = $Force + } + + if ($PSCmdlet.ShouldProcess("Create Zip Package")) { + New-ZipPackage @Arguments + } + } + "zip-pdb" { + $Arguments = @{ + PackageNameSuffix = $NameSuffix + PackageSourcePath = $Source + PackageVersion = $Version + Force = $Force + } + + if ($PSCmdlet.ShouldProcess("Create Symbols Zip Package")) { + New-PdbZipPackage @Arguments + } + } + "min-size" { + # Remove symbol files, xml document files. + Remove-Item "$Source\*.pdb", "$Source\*.xml" -Force + + # Add suffix '-gc' because this package is for the Guest Config team. + if ($Environment.IsWindows) { + $Arguments = @{ + PackageNameSuffix = "$NameSuffix-gc" + PackageSourcePath = $Source + PackageVersion = $Version + Force = $Force + } + + if ($PSCmdlet.ShouldProcess("Create Zip Package")) { + New-ZipPackage @Arguments + } + } + elseif ($Environment.IsLinux) { + $Arguments = @{ + PackageSourcePath = $Source + Name = $Name + PackageNameSuffix = 'gc' + Version = $Version + Force = $Force + } + + if ($PSCmdlet.ShouldProcess("Create tar.gz Package")) { + New-TarballPackage @Arguments + } + } + } + { $_ -like "fxdependent*" } { + ## Remove PDBs from package to reduce size. + if(-not $IncludeSymbols.IsPresent) { + Get-ChildItem $Source -Filter *.pdb | Remove-Item -Force + } + + if ($Environment.IsWindows) { + $Arguments = @{ + PackageNameSuffix = $NameSuffix + PackageSourcePath = $Source + PackageVersion = $Version + Force = $Force + } + + if ($PSCmdlet.ShouldProcess("Create Zip Package")) { + New-ZipPackage @Arguments + } + } elseif ($Environment.IsLinux) { + $Arguments = @{ + PackageSourcePath = $Source + Name = $Name + PackageNameSuffix = 'fxdependent' + Version = $Version + Force = $Force + } + + if ($PSCmdlet.ShouldProcess("Create tar.gz Package")) { + New-TarballPackage @Arguments + } + } + } + "msi" { + $TargetArchitecture = "x64" + if ($Runtime -match "-x86") { + $TargetArchitecture = "x86" + } + Write-Verbose "TargetArchitecture = $TargetArchitecture" -Verbose + + $Arguments = @{ + ProductNameSuffix = $NameSuffix + ProductSourcePath = $Source + ProductVersion = $Version + AssetsPath = "$RepoRoot\assets" + ProductTargetArchitecture = $TargetArchitecture + Force = $Force + } + + if ($PSCmdlet.ShouldProcess("Create MSI Package")) { + New-MSIPackage @Arguments + } + } + "msix" { + $Arguments = @{ + ProductNameSuffix = $NameSuffix + ProductSourcePath = $Source + ProductVersion = $Version + Architecture = $WindowsRuntime.Split('-')[1] + Force = $Force + } + + if ($PSCmdlet.ShouldProcess("Create MSIX Package")) { + New-MSIXPackage @Arguments + } + } + 'nupkg' { + $Arguments = @{ + PackageNameSuffix = $NameSuffix + PackageSourcePath = $Source + PackageVersion = $Version + PackageRuntime = $Runtime + PackageConfiguration = $Configuration + Force = $Force + } + + if ($PSCmdlet.ShouldProcess("Create NuPkg Package")) { + New-NugetContentPackage @Arguments + } + } + "tar" { + $Arguments = @{ + PackageSourcePath = $Source + Name = $Name + Version = $Version + Force = $Force + } + + if ($MacOSRuntime) { + $Arguments['Architecture'] = $MacOSRuntime.Split('-')[1] + } + + if ($PSCmdlet.ShouldProcess("Create tar.gz Package")) { + New-TarballPackage @Arguments + } + } + "tar-arm" { + $Arguments = @{ + PackageSourcePath = $Source + Name = $Name + Version = $Version + Force = $Force + Architecture = "arm32" + ExcludeSymbolicLinks = $true + } + + if ($PSCmdlet.ShouldProcess("Create tar.gz Package")) { + New-TarballPackage @Arguments + } + } + "tar-arm64" { + $Arguments = @{ + PackageSourcePath = $Source + Name = $Name + Version = $Version + Force = $Force + Architecture = "arm64" + ExcludeSymbolicLinks = $true + } + + if ($PSCmdlet.ShouldProcess("Create tar.gz Package")) { + New-TarballPackage @Arguments + } + } + "tar-alpine" { + $Arguments = @{ + PackageSourcePath = $Source + Name = $Name + Version = $Version + Force = $Force + Architecture = "alpine-x64" + ExcludeSymbolicLinks = $true + } + + if ($PSCmdlet.ShouldProcess("Create tar.gz Package")) { + New-TarballPackage @Arguments + } + } + 'deb' { + $Arguments = @{ + Type = 'deb' + PackageSourcePath = $Source + Name = $Name + Version = $Version + Force = $Force + NoSudo = $NoSudo + LTS = $LTS + } + foreach ($Distro in $Script:DebianDistributions) { + $Arguments["Distribution"] = $Distro + if ($PSCmdlet.ShouldProcess("Create DEB Package for $Distro")) { + New-UnixPackage @Arguments + } + } + } + 'rpm' { + $Arguments = @{ + Type = 'rpm' + PackageSourcePath = $Source + Name = $Name + Version = $Version + Force = $Force + NoSudo = $NoSudo + LTS = $LTS + } + foreach ($Distro in $Script:RedhatDistributions) { + $Arguments["Distribution"] = $Distro + if ($PSCmdlet.ShouldProcess("Create RPM Package for $Distro")) { + New-UnixPackage @Arguments + } + } + } + default { + $Arguments = @{ + Type = $_ + PackageSourcePath = $Source + Name = $Name + Version = $Version + Force = $Force + NoSudo = $NoSudo + LTS = $LTS + } + + if ($PSCmdlet.ShouldProcess("Create $_ Package")) { + New-UnixPackage @Arguments + } + } + } + + if ($IncludeSymbols.IsPresent) + { + # Source is a temporary folder when -IncludeSymbols is present. So, we should remove it. + Remove-Item -Path $Source -Recurse -Force -ErrorAction SilentlyContinue + } + } +} + +function New-UnixPackage { + [CmdletBinding(SupportsShouldProcess=$true)] + param( + [Parameter(Mandatory)] + [ValidateSet("deb", "osxpkg", "rpm")] + [string]$Type, + + [Parameter(Mandatory)] + [string]$PackageSourcePath, + + # Must start with 'powershell' but may have any suffix + [Parameter(Mandatory)] + [ValidatePattern("^powershell")] + [string]$Name, + + [Parameter(Mandatory)] + [string]$Version, + + # Package iteration version (rarely changed) + # This is a string because strings are appended to it + [string]$Iteration = "1", + + [Switch] + $Force, + + [switch] + $NoSudo, + + [switch] + $LTS, + + [string] + $CurrentLocation = (Get-Location) + ) + + DynamicParam { + if ($Type -eq "deb" -or $Type -eq 'rpm') { + # Add a dynamic parameter '-Distribution' when the specified package type is 'deb'. + # The '-Distribution' parameter can be used to indicate which Debian distro this pacakge is targeting. + $ParameterAttr = New-Object "System.Management.Automation.ParameterAttribute" + if($type -eq 'deb') + { + $ValidateSetAttr = New-Object "System.Management.Automation.ValidateSetAttribute" -ArgumentList $Script:DebianDistributions + } + else + { + $ValidateSetAttr = New-Object "System.Management.Automation.ValidateSetAttribute" -ArgumentList $Script:RedHatDistributions + } + $Attributes = New-Object "System.Collections.ObjectModel.Collection``1[System.Attribute]" + $Attributes.Add($ParameterAttr) > $null + $Attributes.Add($ValidateSetAttr) > $null + + $Parameter = New-Object "System.Management.Automation.RuntimeDefinedParameter" -ArgumentList ("Distribution", [string], $Attributes) + $Dict = New-Object "System.Management.Automation.RuntimeDefinedParameterDictionary" + $Dict.Add("Distribution", $Parameter) > $null + return $Dict + } + } + + End { + # This allows sudo install to be optional; needed when running in containers / as root + # Note that when it is null, Invoke-Expression (but not &) must be used to interpolate properly + $sudo = if (!$NoSudo) { "sudo" } + + # Validate platform + $ErrorMessage = "Must be on {0} to build '$Type' packages!" + switch ($Type) { + "deb" { + $packageVersion = Get-LinuxPackageSemanticVersion -Version $Version + if (!$Environment.IsUbuntu -and !$Environment.IsDebian) { + throw ($ErrorMessage -f "Ubuntu or Debian") + } + + if ($PSBoundParameters.ContainsKey('Distribution')) { + $DebDistro = $PSBoundParameters['Distribution'] + } elseif ($Environment.IsUbuntu16) { + $DebDistro = "ubuntu.16.04" + } elseif ($Environment.IsUbuntu18) { + $DebDistro = "ubuntu.18.04" + } elseif ($Environment.IsUbuntu20) { + $DebDistro = "ubuntu.20.04" + } elseif ($Environment.IsDebian9) { + $DebDistro = "debian.9" + } else { + throw "The current Debian distribution is not supported." + } + + # iteration is "debian_revision" + # usage of this to differentiate distributions is allowed by non-standard + $Iteration += ".$DebDistro" + } + "rpm" { + if ($PSBoundParameters.ContainsKey('Distribution')) { + $DebDistro = $PSBoundParameters['Distribution'] + + } elseif ($Environment.IsRedHatFamily) { + $DebDistro = "rhel.7" + } else { + throw "The current distribution is not supported." + } + + $packageVersion = Get-LinuxPackageSemanticVersion -Version $Version + } + "osxpkg" { + $packageVersion = $Version + if (!$Environment.IsMacOS) { + throw ($ErrorMessage -f "macOS") + } + + $DebDistro = 'macOS' + } + } + + # Determine if the version is a preview version + $IsPreview = Test-IsPreview -Version $Version -IsLTS:$LTS + + # Preview versions have preview in the name + $Name = if($LTS) { + "powershell-lts" + } + elseif ($IsPreview) { + "powershell-preview" + } + else { + "powershell" + } + + # Verify dependencies are installed and in the path + Test-Dependencies + + $Description = $packagingStrings.Description + + # Break the version down into its components, we are interested in the major version + $VersionMatch = [regex]::Match($Version, '(\d+)(?:.(\d+)(?:.(\d+)(?:-preview(?:.(\d+))?)?)?)?') + $MajorVersion = $VersionMatch.Groups[1].Value + + # Suffix is used for side-by-side preview/release package installation + $Suffix = if ($IsPreview) { $MajorVersion + "-preview" } elseif ($LTS) { $MajorVersion + "-lts" } else { $MajorVersion } + + # Setup staging directory so we don't change the original source directory + $Staging = "$PSScriptRoot/staging" + if ($PSCmdlet.ShouldProcess("Create staging folder")) { + New-StagingFolder -StagingPath $Staging -PackageSourcePath $PackageSourcePath + } + + # Follow the Filesystem Hierarchy Standard for Linux and macOS + $Destination = if ($Environment.IsLinux) { + "/opt/microsoft/powershell/$Suffix" + } elseif ($Environment.IsMacOS) { + "/usr/local/microsoft/powershell/$Suffix" + } + + # Destination for symlink to powershell executable + $Link = Get-PwshExecutablePath -IsPreview:$IsPreview + $links = @(New-LinkInfo -LinkDestination $Link -LinkTarget "$Destination/pwsh") + + if($LTS) { + $links += New-LinkInfo -LinkDestination (Get-PwshExecutablePath -IsLTS:$LTS) -LinkTarget "$Destination/pwsh" + } + + if ($PSCmdlet.ShouldProcess("Create package file system")) + { + # Generate After Install and After Remove scripts + $AfterScriptInfo = New-AfterScripts -Link $Link -Distribution $DebDistro -Destination $Destination + + # there is a weird bug in fpm + # if the target of the powershell symlink exists, `fpm` aborts + # with a `utime` error on macOS. + # so we move it to make symlink broken + # refers to executable, does not vary by channel + $symlink_dest = "$Destination/pwsh" + $hack_dest = "./_fpm_symlink_hack_powershell" + if ($Environment.IsMacOS) { + if (Test-Path $symlink_dest) { + Write-Warning "Move $symlink_dest to $hack_dest (fpm utime bug)" + Start-NativeExecution ([ScriptBlock]::Create("$sudo mv $symlink_dest $hack_dest")) + } + } + + # Generate gzip of man file + $ManGzipInfo = New-ManGzip -IsPreview:$IsPreview -IsLTS:$LTS + + # Change permissions for packaging + Write-Log "Setting permissions..." + Start-NativeExecution { + find $Staging -type d | xargs chmod 755 + find $Staging -type f | xargs chmod 644 + chmod 644 $ManGzipInfo.GzipFile + # refers to executable, does not vary by channel + chmod 755 "$Staging/pwsh" #only the executable file should be granted the execution permission + } + } + + # Add macOS powershell launcher + if ($Type -eq "osxpkg") + { + Write-Log "Adding macOS launch application..." + if ($PSCmdlet.ShouldProcess("Add macOS launch application")) + { + # Generate launcher app folder + $AppsFolder = New-MacOSLauncher -Version $Version + } + } + + $packageDependenciesParams = @{} + if ($DebDistro) + { + $packageDependenciesParams['Distribution']=$DebDistro + } + + # Setup package dependencies + $Dependencies = @(Get-PackageDependencies @packageDependenciesParams) + + $Arguments = Get-FpmArguments ` + -Name $Name ` + -Version $packageVersion ` + -Iteration $Iteration ` + -Description $Description ` + -Type $Type ` + -Dependencies $Dependencies ` + -AfterInstallScript $AfterScriptInfo.AfterInstallScript ` + -AfterRemoveScript $AfterScriptInfo.AfterRemoveScript ` + -Staging $Staging ` + -Destination $Destination ` + -ManGzipFile $ManGzipInfo.GzipFile ` + -ManDestination $ManGzipInfo.ManFile ` + -LinkInfo $Links ` + -AppsFolder $AppsFolder ` + -Distribution $DebDistro ` + -ErrorAction Stop + + # Build package + try { + if ($PSCmdlet.ShouldProcess("Create $type package")) { + Write-Log "Creating package with fpm..." + $Output = Start-NativeExecution { fpm $Arguments } + } + } finally { + if ($Environment.IsMacOS) { + Write-Log "Starting Cleanup for mac packaging..." + if ($PSCmdlet.ShouldProcess("Cleanup macOS launcher")) + { + Clear-MacOSLauncher + } + + # this is continuation of a fpm hack for a weird bug + if (Test-Path $hack_dest) { + Write-Warning "Move $hack_dest to $symlink_dest (fpm utime bug)" + Start-NativeExecution -sb ([ScriptBlock]::Create("$sudo mv $hack_dest $symlink_dest")) -VerboseOutputOnError + } + } + if ($AfterScriptInfo.AfterInstallScript) { + Remove-Item -ErrorAction 'silentlycontinue' $AfterScriptInfo.AfterInstallScript -Force + } + if ($AfterScriptInfo.AfterRemoveScript) { + Remove-Item -ErrorAction 'silentlycontinue' $AfterScriptInfo.AfterRemoveScript -Force + } + Remove-Item -Path $ManGzipInfo.GzipFile -Force -ErrorAction SilentlyContinue + } + + # Magic to get path output + $createdPackage = Get-Item (Join-Path $CurrentLocation (($Output[-1] -split ":path=>")[-1] -replace '["{}]')) + + if ($Environment.IsMacOS) { + if ($PSCmdlet.ShouldProcess("Add distribution information and Fix PackageName")) + { + $createdPackage = New-MacOsDistributionPackage -FpmPackage $createdPackage -IsPreview:$IsPreview + } + } + + if (Test-Path $createdPackage) + { + Write-Verbose "Created package: $createdPackage" -Verbose + return $createdPackage + } + else + { + throw "Failed to create $createdPackage" + } + } +} diff --git a/test/perf/benchmarks/powershell-perf.csproj b/test/perf/benchmarks/powershell-perf.csproj index 78608c2e96f..93c164b98b8 100644 --- a/test/perf/benchmarks/powershell-perf.csproj +++ b/test/perf/benchmarks/powershell-perf.csproj @@ -46,6 +46,7 @@ + @@ -59,6 +60,8 @@ + + diff --git a/test/perf/dotnet-tools/BenchmarkDotNet.Extensions/BenchmarkDotNet.Extensions.csproj b/test/perf/dotnet-tools/BenchmarkDotNet.Extensions/BenchmarkDotNet.Extensions.csproj index 1383cfc1d62..02a372fa115 100644 --- a/test/perf/dotnet-tools/BenchmarkDotNet.Extensions/BenchmarkDotNet.Extensions.csproj +++ b/test/perf/dotnet-tools/BenchmarkDotNet.Extensions/BenchmarkDotNet.Extensions.csproj @@ -6,8 +6,8 @@ - - + + diff --git a/test/perf/dotnet-tools/Reporting/Reporting.csproj b/test/perf/dotnet-tools/Reporting/Reporting.csproj index ca56fc51efc..70447cf5d73 100644 --- a/test/perf/dotnet-tools/Reporting/Reporting.csproj +++ b/test/perf/dotnet-tools/Reporting/Reporting.csproj @@ -1,4 +1,4 @@ - + Library @@ -6,8 +6,8 @@ - - + + diff --git a/test/perf/dotnet-tools/ResultsComparer/Program.cs b/test/perf/dotnet-tools/ResultsComparer/Program.cs index 655c9f3458e..68615f61c78 100644 --- a/test/perf/dotnet-tools/ResultsComparer/Program.cs +++ b/test/perf/dotnet-tools/ResultsComparer/Program.cs @@ -20,7 +20,7 @@ namespace ResultsComparer { - public class Program + public sealed class Program { private const string FullBdnJsonFileExtension = "full.json"; diff --git a/test/perf/dotnet-tools/ResultsComparer/ResultsComparer.csproj b/test/perf/dotnet-tools/ResultsComparer/ResultsComparer.csproj index 9934bd0df21..c40f05d7b77 100644 --- a/test/perf/dotnet-tools/ResultsComparer/ResultsComparer.csproj +++ b/test/perf/dotnet-tools/ResultsComparer/ResultsComparer.csproj @@ -1,4 +1,4 @@ - + Exe $(PERFLAB_TARGET_FRAMEWORKS) @@ -6,10 +6,10 @@ latest - + - - + + diff --git a/test/powershell/Host/ConsoleHost.Tests.ps1 b/test/powershell/Host/ConsoleHost.Tests.ps1 index 5c903f53745..ccec3d7455f 100644 --- a/test/powershell/Host/ConsoleHost.Tests.ps1 +++ b/test/powershell/Host/ConsoleHost.Tests.ps1 @@ -414,20 +414,21 @@ export $envVarName='$guid' It "text output" { # Join (multiple lines) and remove whitespace (we don't care about spacing) to verify we converted to string (by generating a table) - -join (& $powershell -noprofile -outputFormat text { [PSCustomObject]@{X=10;Y=20} }) -replace "\s","" | Should -Be "XY--1020" + -join (& $powershell -noprofile -outputFormat text { $PSStyle.OutputRendering = 'PlainText'; [PSCustomObject]@{X=10;Y=20} }) -replace "\s","" | Should -Be "XY--1020" } It "errors are in text if error is redirected, encoded command, non-interactive, and outputformat specified" { $p = [Diagnostics.Process]::new() $p.StartInfo.FileName = "pwsh" - $encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes('$ErrorView="NormalView";throw "boom"')) + $encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes('throw "boom"')) $p.StartInfo.Arguments = "-EncodedCommand $encoded -ExecutionPolicy Bypass -NoLogo -NonInteractive -NoProfile -OutputFormat text" $p.StartInfo.UseShellExecute = $false $p.StartInfo.RedirectStandardError = $true $p.Start() | Out-Null $out = $p.StandardError.ReadToEnd() $out | Should -Not -BeNullOrEmpty - $out.Split([Environment]::NewLine)[0] | Should -BeExactly "boom" + $out = $out.Split([Environment]::NewLine)[0] + [System.Management.Automation.Internal.StringDecorated]::new($out).ToString("PlainText") | Should -BeExactly "Exception: boom" } } @@ -491,7 +492,15 @@ export $envVarName='$guid' } Context "Redirected standard input for 'interactive' use" { - $nl = [Environment]::Newline + BeforeAll { + $nl = [Environment]::Newline + $oldColor = $env:NO_COLOR + $env:NO_COLOR = 1 + } + + AfterAll { + $env:NO_COLOR = $oldColor + } # All of the following tests replace the prompt (either via an initial command or interactively) # so that we can read StandardOutput and reliably know exactly what the prompt is. @@ -586,6 +595,8 @@ foo It "Redirected input w/ nested prompt" -Pending:($IsWindows) { $si = NewProcessStartInfo "-noprofile -noexit -c ""`$function:prompt = { 'PS' + ('>'*(`$NestedPromptLevel+1)) + ' ' }""" -RedirectStdIn $process = RunPowerShell $si + $process.StandardInput.Write("`$PSStyle.OutputRendering='plaintext'`n") + $null = $process.StandardOutput.ReadLine() $process.StandardInput.Write("`$Host.EnterNestedPrompt()`n") $process.StandardOutput.ReadLine() | Should -Be "PS> `$Host.EnterNestedPrompt()" $process.StandardInput.Write("exit`n") @@ -664,6 +675,20 @@ namespace StackTest { It "Should start if HOME is not defined" -Skip:($IsWindows) { bash -c "unset HOME;$powershell -c '1+1'" | Should -BeExactly 2 } + + It "Same user should use the same temporary HOME directory for different sessions" -Skip:($IsWindows) { + $results = bash -c @" +unset HOME; +$powershell -c '[System.Management.Automation.Platform]::SelectProductNameForDirectory([System.Management.Automation.Platform+XDG_Type]::DEFAULT)'; +$powershell -c '[System.Management.Automation.Platform]::SelectProductNameForDirectory([System.Management.Automation.Platform+XDG_Type]::DEFAULT)'; +"@ + $results | Should -HaveCount 2 + $results[0] | Should -BeExactly $results[1] + + $tempHomeName = "pwsh-{0}-98288ff9-5712-4a14-9a11-23693b9cd91a" -f [System.Environment]::UserName + $defaultPath = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath "$tempHomeName/.config/powershell" + $results[0] | Should -BeExactly $defaultPath + } } Context "PATH environment variable" { @@ -833,6 +858,64 @@ namespace StackTest { $LASTEXITCODE | Should -Be $ExitCodeBadCommandLineParameter } } + + Context "Startup banner text tests" -Tag Slow { + BeforeAll { + $outputPath = "Temp:\StartupBannerTest-Output-${Pid}.txt" + $inputPath = "Temp:\StartupBannerTest-Input.txt" + "exit" > $inputPath + + # Not testing update notification banner text here + $oldPowerShellUpdateCheck = $env:POWERSHELL_UPDATECHECK + $env:POWERSHELL_UPDATECHECK = "Off" + + # Set TERM to "dumb" to avoid DECCKM codes in the output + $oldTERM = $env:TERM + $env:TERM = "dumb" + + $escPwd = [regex]::Escape($pwd) + $expectedPromptPattern = "^PS ${escPwd}> exit`$" + + $spArgs = @{ + FilePath = $powershell + ArgumentList = @("-NoProfile") + RedirectStandardInput = $inputPath + RedirectStandardOutput = $outputPath + WorkingDirectory = $pwd + PassThru = $true + NoNewWindow = $true + UseNewEnvironment = $false + } + } + AfterAll { + $env:TERM = $oldTERM + $env:POWERSHELL_UPDATECHECK = $oldPowerShellUpdateCheck + + Remove-Item $inputPath -Force -ErrorAction Ignore + Remove-Item $outputPath -Force -ErrorAction Ignore + } + BeforeEach { + Remove-Item $outputPath -Force -ErrorAction Ignore + } + It "Displays expected startup banner text by default" { + $process = Start-Process @spArgs + Wait-UntilTrue -sb { $process.HasExited } -TimeoutInMilliseconds 5000 -IntervalInMilliseconds 250 | Should -BeTrue + + $out = @(Get-Content $outputPath) + $out.Count | Should -Be 2 + $out[0] | Should -BeExactly "PowerShell $($PSVersionTable.GitCommitId)" + $out[1] | Should -MatchExactly $expectedPromptPattern + } + It "Displays only the prompt with -NoLogo" { + $spArgs["ArgumentList"] += "-NoLogo" + $process = Start-Process @spArgs + Wait-UntilTrue -sb { $process.HasExited } -TimeoutInMilliseconds 5000 -IntervalInMilliseconds 250 | Should -BeTrue + + $out = @(Get-Content $outputPath) + $out.Count | Should -Be 1 + $out[0] | Should -MatchExactly $expectedPromptPattern + } + } } Describe "WindowStyle argument" -Tag Feature { @@ -1024,8 +1107,7 @@ Describe 'Pwsh startup and PATH' -Tag CI { } Describe 'Console host name' -Tag CI { - It 'Name is pwsh' -Pending { - # waiting on https://github.com/dotnet/runtime/issues/33673 + It 'Name is pwsh' { (Get-Process -Id $PID).Name | Should -BeExactly 'pwsh' } } diff --git a/test/powershell/Host/Logging.Tests.ps1 b/test/powershell/Host/Logging.Tests.ps1 index 5250d2ef5ae..628bd8ac95e 100644 --- a/test/powershell/Host/Logging.Tests.ps1 +++ b/test/powershell/Host/Logging.Tests.ps1 @@ -188,7 +188,9 @@ Creating Scriptblock text \(1 of 1\):#012{0}(⏎|#012)*ScriptBlock ID: [0-9a-z\- } } - It 'Verifies scriptblock logging' -Skip:(!$IsSupportedEnvironment) { + # Skip test as it is failing in PowerShell CI on Linux platform. + # Tracking Issue: https://github.com/PowerShell/PowerShell/issues/17092 + It 'Verifies scriptblock logging' -Skip <#-Skip:(!$IsSupportedEnvironment)#> { $configFile = WriteLogSettings -LogId $logId -ScriptBlockLogging -LogLevel Verbose $script = @' $PID @@ -217,7 +219,9 @@ $PID $createdEvents[2].Message | Should -Match ($scriptBlockCreatedRegExTemplate -f "Write\-Verbose 'testheader123' ;Write\-verbose 'after'") } - It 'Verifies scriptblock logging with null character' -Skip:(!$IsSupportedEnvironment) { + # Skip test as it is failing in PowerShell CI on Linux platform. + # Tracking Issue: https://github.com/PowerShell/PowerShell/issues/17092 + It 'Verifies scriptblock logging with null character' -Skip <#-Skip:(!$IsSupportedEnvironment)#> { $configFile = WriteLogSettings -LogId $logId -ScriptBlockLogging -LogLevel Verbose $script = @' $PID diff --git a/test/powershell/Host/PSVersionTable.Tests.ps1 b/test/powershell/Host/PSVersionTable.Tests.ps1 index e691b7661be..3f6ccfcd8ba 100644 --- a/test/powershell/Host/PSVersionTable.Tests.ps1 +++ b/test/powershell/Host/PSVersionTable.Tests.ps1 @@ -23,7 +23,7 @@ Describe "PSVersionTable" -Tags "CI" { $unexpectectGitCommitIdPattern = $fullVersionPattern } - $powerShellVersions = "1.0", "2.0", "3.0", "4.0", "5.0", "5.1", "6.0", "6.1", "6.2", "7.0", "7.1", "7.2" + $powerShellVersions = "1.0", "2.0", "3.0", "4.0", "5.0", "5.1", "6.0", "6.1", "6.2", "7.0", "7.1", "7.2", "7.3" $powerShellCompatibleVersions = $PSVersionTable.PSCompatibleVersions | ForEach-Object {$_.ToString(2).SubString(0,3)} } diff --git a/test/powershell/Host/Startup.Tests.ps1 b/test/powershell/Host/Startup.Tests.ps1 index bb775d0ca2f..ddaa2736a5c 100644 --- a/test/powershell/Host/Startup.Tests.ps1 +++ b/test/powershell/Host/Startup.Tests.ps1 @@ -7,7 +7,6 @@ Describe "Validate start of console host" -Tag CI { 'Microsoft.ApplicationInsights.dll' 'Microsoft.Management.Infrastructure.dll' 'Microsoft.PowerShell.ConsoleHost.dll' - 'Microsoft.PowerShell.Security.dll' 'Microsoft.Win32.Primitives.dll' 'Microsoft.Win32.Registry.dll' 'netstandard.dll' @@ -15,7 +14,6 @@ Describe "Validate start of console host" -Tag CI { 'pwsh.dll' 'System.Collections.Concurrent.dll' 'System.Collections.dll' - 'System.Collections.NonGeneric.dll' 'System.Collections.Specialized.dll' 'System.ComponentModel.dll' 'System.ComponentModel.Primitives.dll' @@ -44,15 +42,12 @@ Describe "Validate start of console host" -Tag CI { 'System.Reflection.Primitives.dll' 'System.Runtime.dll' 'System.Runtime.InteropServices.dll' - 'System.Runtime.InteropServices.RuntimeInformation.dll' 'System.Runtime.Loader.dll' 'System.Runtime.Numerics.dll' 'System.Runtime.Serialization.Formatters.dll' 'System.Runtime.Serialization.Primitives.dll' 'System.Security.AccessControl.dll' - 'System.Security.Cryptography.Encoding.dll' - 'System.Security.Cryptography.Primitives.dll' - 'System.Security.Cryptography.X509Certificates.dll' + 'System.Security.Cryptography.dll' 'System.Security.Principal.Windows.dll' 'System.Text.Encoding.Extensions.dll' 'System.Text.RegularExpressions.dll' diff --git a/test/powershell/Host/TabCompletion/BugFix.Tests.ps1 b/test/powershell/Host/TabCompletion/BugFix.Tests.ps1 index 78be093a5bb..3cc44f3c0e1 100644 --- a/test/powershell/Host/TabCompletion/BugFix.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/BugFix.Tests.ps1 @@ -85,8 +85,7 @@ Describe "Tab completion bug fix" -Tags "CI" { $result.CurrentMatchIndex | Should -Be -1 $result.ReplacementIndex | Should -Be 40 $result.ReplacementLength | Should -Be 0 - $result.CompletionMatches[0].CompletionText | Should -BeExactly 'Expression' - $result.CompletionMatches[1].CompletionText | Should -BeExactly 'Ascending' - $result.CompletionMatches[2].CompletionText | Should -BeExactly 'Descending' + $result.CompletionMatches[0].CompletionText | Should -BeExactly 'Ascending' + $result.CompletionMatches[1].CompletionText | Should -BeExactly 'Descending' } } diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 2674a74a21a..02a90fd65d2 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -43,6 +43,22 @@ Describe "TabCompletion" -Tags CI { $res.CompletionMatches[0].CompletionText | Should -BeExactly 'CompareTo(' } + It 'should complete generic type parameters for static methods' { + $script = '[array]::Empty[pscu' + + $results = TabExpansion2 -inputScript $script -cursorColumn $script.Length + $results.CompletionMatches.CompletionText | Should -Contain 'pscustomobject' + } + + It 'should complete generic type parameters for instance methods' { + $script = ' + $dict = [System.Collections.Concurrent.ConcurrentDictionary[string, int]]::new() + $dict.AddOrUpdate[pscu' + + $results = TabExpansion2 -inputScript $script -cursorColumn $script.Length + $results.CompletionMatches.CompletionText | Should -Contain 'pscustomobject' + } + It 'Should complete Magic foreach' { $res = TabExpansion2 -inputScript '(1..10).Fo' -cursorColumn '(1..10).Fo'.Length $res.CompletionMatches[0].CompletionText | Should -BeExactly 'ForEach(' @@ -58,6 +74,141 @@ Describe "TabCompletion" -Tags CI { $res.CompletionMatches[0].CompletionText | Should -BeExactly 'pscustomobject' } + foreach ($Operator in [System.Management.Automation.CompletionCompleters]::CompleteOperator("")) + { + It "Should complete $($Operator.CompletionText)" { + $res = TabExpansion2 -inputScript "'' $($Operator.CompletionText)" -cursorColumn ($Operator.CompletionText.Length + 3) + $res.CompletionMatches[0].CompletionText | Should -BeExactly $Operator.CompletionText + } + } + + It 'should complete index expression for ' -TestCases @( + @{ + Intent = 'Hashtable with no user input' + Expected = "'PSVersion'" + TestString = '$PSVersionTable[^' + } + @{ + Intent = 'Hashtable with partial input' + Expected = "'PSVersion'" + TestString = '$PSVersionTable[ PSvers^' + } + @{ + Intent = 'Hashtable with partial quoted input' + Expected = "'PSVersion'" + TestString = '$PSVersionTable["PSvers^' + } + @{ + Intent = 'Hashtable from Ast' + Expected = "'Hello'" + TestString = '$Table = @{Hello = "World"};$Table[^' + } + @{ + Intent = 'Hashtable with cursor on new line' + Expected = "'Hello'" + TestString = @' +$Table = @{Hello = "World"} +$Table[ +^ +'@ + } + ) -Test { + param($Expected, $TestString) + $CursorIndex = $TestString.IndexOf('^') + $res = TabExpansion2 -cursorColumn $CursorIndex -inputScript $TestString.Remove($CursorIndex, 1) + $res.CompletionMatches[0].CompletionText | Should -BeExactly $Expected + } + + it 'should add quotes when completing hashtable key from Ast with member syntax' -Test { + $res = TabExpansion2 -inputScript '$Table = @{"Hello World" = "World"};$Table.' + $res.CompletionMatches.CompletionText | Where-Object {$_ -eq "'Hello World'"} | Should -BeExactly "'Hello World'" + } + + It '' -TestCases @( + @{ + Intent = 'Complete member with space between dot and cursor' + Expected = 'value__' + TestString = '[System.Management.Automation.ActionPreference]::Break. ^' + } + @{ + Intent = 'Complete member when cursor is in-between existing members and spaces' + Expected = 'value__' + TestString = '[System.Management.Automation.ActionPreference]::Break. ^ ToString()' + } + @{ + Intent = 'Complete static member with space between colons and cursor' + Expected = 'Break' + TestString = '[System.Management.Automation.ActionPreference]:: ^' + } + @{ + Intent = 'Complete static member with new line between colons and cursor' + Expected = 'Break' + TestString = @' +[System.Management.Automation.ActionPreference]:: +^ +'@ + } + @{ + Intent = 'Complete static member with partial input and incomplete input at end of line' + Expected = 'Break' + TestString = '[System.Management.Automation.ActionPreference]:: Brea^. value__.' + } + @{ + Intent = 'Complete static member with partial input and valid input at end of line' + Expected = 'Break' + TestString = '[System.Management.Automation.ActionPreference]:: Brea^. value__' + } + @{ + Intent = 'Complete member with new line between colons and cursor' + Expected = 'value__' + TestString = '[System.Management.Automation.ActionPreference]::Break. ^ ToString()' + } + @{ + Intent = 'Complete type with incomplete expression input at end of line' + Expected = 'System.Management.Automation.ActionPreference' + TestString = '[System.Management.Automation.ActionPreference^]::' + } + @{ + Intent = 'Complete member inside switch expression' + Expected = 'Length' + TestString = @' +switch ($x) +{ + 'RandomString'.^ + {} +} +'@ + } + @{ + Intent = 'Complete member in commandast' + Expected = 'Length' + TestString = 'ls "".^' + } + ){ + param($Expected, $TestString) + $CursorIndex = $TestString.IndexOf('^') + $res = TabExpansion2 -cursorColumn $CursorIndex -inputScript $TestString.Remove($CursorIndex, 1) + $res.CompletionMatches[0].CompletionText | Should -BeExactly $Expected + } + + It 'Should Complete and replace existing member with space in front of cursor and cursor in front of word' { + $TestString = '[System.Management.Automation.ActionPreference]:: ^Break' + $CursorIndex = $TestString.IndexOf('^') + $res = TabExpansion2 -cursorColumn $CursorIndex -inputScript $TestString.Remove($CursorIndex, 1) + $res.ReplacementIndex | Should -BeExactly $CursorIndex + $res.ReplacementLength | Should -Be 5 + $res.CompletionMatches[0].CompletionText | Should -BeExactly 'Break' + } + + It 'Complete and replace existing member with colons in front of cursor and cursor in front of word' { + $TestString = '[System.Management.Automation.ActionPreference]::^Break' + $CursorIndex = $TestString.IndexOf('^') + $res = TabExpansion2 -cursorColumn $CursorIndex -inputScript $TestString.Remove($CursorIndex, 1) + $res.ReplacementIndex | Should -BeExactly $CursorIndex + $res.ReplacementLength | Should -Be 5 + $res.CompletionMatches[0].CompletionText | Should -BeExactly 'Break' + } + It 'Should complete namespaces' { $res = TabExpansion2 -inputScript 'using namespace Sys' -cursorColumn 'using namespace Sys'.Length $res.CompletionMatches[0].CompletionText | Should -BeExactly 'System' @@ -128,6 +279,50 @@ Describe "TabCompletion" -Tags CI { $res.CompletionMatches | Should -HaveCount 3 $res.CompletionMatches.CompletionText -join ' ' | Should -BeExactly 'A B C' } + It 'Complete hashtable key without duplicate keys' { + class X { + $A + $B + $C + } + $TestString = '[x]@{A="";^}' + $CursorIndex = $TestString.IndexOf('^') + $res = TabExpansion2 -inputScript $TestString.Remove($CursorIndex, 1) -cursorColumn $CursorIndex + $res.CompletionMatches | Should -HaveCount 2 + $res.CompletionMatches.CompletionText -join ' ' | Should -BeExactly 'B C' + } + It 'Complete hashtable key on empty line after key/value pair' { + class X { + $A + $B + $C + } + $TestString = @' +[x]@{ + B="" + ^ +} +'@ + $CursorIndex = $TestString.IndexOf('^') + $res = TabExpansion2 -inputScript $TestString.Remove($CursorIndex, 1) -cursorColumn $CursorIndex + $res.CompletionMatches | Should -HaveCount 2 + $res.CompletionMatches.CompletionText -join ' ' | Should -BeExactly 'A C' + } + + It 'Complete hashtable keys for Get-WinEvent FilterHashtable' -Skip:(!$IsWindows) { + $TestString = 'Get-WinEvent -FilterHashtable @{^' + $CursorIndex = $TestString.IndexOf('^') + $res = TabExpansion2 -inputScript $TestString.Remove($CursorIndex, 1) -cursorColumn $CursorIndex + $res.CompletionMatches | Should -HaveCount 11 + $res.CompletionMatches.CompletionText -join ' ' | Should -BeExactly 'LogName ProviderName Path Keywords ID Level StartTime EndTime UserID Data SuppressHashFilter' + } + + It 'Complete hashtable keys for a hashtable used for splatting' { + $TestString = '$GetChildItemParams=@{^};Get-ChildItem @GetChildItemParams -Force -Recurse' + $CursorIndex = $TestString.IndexOf('^') + $res = TabExpansion2 -inputScript $TestString.Remove($CursorIndex, 1) -cursorColumn $CursorIndex + $res.CompletionMatches[0].CompletionText | Should -BeExactly 'Path' + } It 'Should complete "Get-Process -Id " with Id and name in tooltip' { Set-StrictMode -Version 3.0 @@ -163,6 +358,12 @@ Describe "TabCompletion" -Tags CI { $res.CompletionMatches[1].CompletionText | Should -BeExactly '-Functionality' } + It 'Should not remove braces when completing variable with braces' { + $Text = '"Hello${psversiont}World"' + $res = TabExpansion2 -inputScript $Text -cursorColumn $Text.IndexOf('p') + $res.CompletionMatches[0].CompletionText | Should -BeExactly '${PSVersionTable}' + } + It 'Should work for variable assignment of enum type: ' -TestCases @( @{ inputStr = '$ErrorActionPreference = '; filter = ''; doubleQuotes = $false } @{ inputStr = '$ErrorActionPreference='; filter = ''; doubleQuotes = $false } @@ -301,6 +502,75 @@ Describe "TabCompletion" -Tags CI { $actual | Should -BeExactly $expected } + It 'ForEach-Object member completion results should include methods' { + $res = TabExpansion2 -inputScript '1..10 | ForEach-Object -MemberName ' + $res.CompletionMatches.CompletionText | Should -Contain "GetType(" + } + + It 'Should not complete void instance members' { + $res = TabExpansion2 -inputScript '([void]("")).' + $res.CompletionMatches | Should -BeNullOrEmpty + } + + It 'Should complete custom constructor from class using the AST' { + $res = TabExpansion2 -inputScript 'class ConstructorTestClass{ConstructorTestClass ([string] $s){}};[ConstructorTestClass]::' + $res.CompletionMatches | Should -HaveCount 3 + $completionText = $res.CompletionMatches.CompletionText | Sort-Object + $completionText -join ' ' | Should -BeExactly 'Equals( new( ReferenceEquals(' + } + + It 'Should show multiple constructors in the tooltip' { + $res = TabExpansion2 -inputScript 'class ConstructorTestClass{ConstructorTestClass ([string] $s){}ConstructorTestClass ([int] $i){}ConstructorTestClass ([int] $i, [bool]$b){}};[ConstructorTestClass]::new' + $res.CompletionMatches | Should -HaveCount 1 + $completionText = $res.CompletionMatches.ToolTip | Should -BeExactly @' +ConstructorTestClass(string s) +ConstructorTestClass(int i) +ConstructorTestClass(int i, bool b) +'@ + } + + It 'Should complete parameter in param block' { + $res = TabExpansion2 -inputScript 'Param($Param1=(Get-ChildItem -))' -cursorColumn 30 + $res.CompletionMatches[0].CompletionText | Should -BeExactly '-Path' + } + + It 'Should complete member in param block' { + $res = TabExpansion2 -inputScript 'Param($Param1=($PSVersionTable.))' -cursorColumn 31 + $res.CompletionMatches[0].CompletionText | Should -BeExactly 'Count' + } + + It 'Should complete attribute argument in param block' { + $res = TabExpansion2 -inputScript 'Param([Parameter()]$Param1)' -cursorColumn 17 + $names = [Parameter].GetProperties() | Where-Object CanWrite | ForEach-Object Name + + $diffs = Compare-Object -ReferenceObject $res.CompletionMatches.CompletionText -DifferenceObject $names + $diffs | Should -BeNullOrEmpty + } + + It 'Should complete argument for second parameter' { + $res = TabExpansion2 -inputScript 'Get-ChildItem -Path $HOME -ErrorAction ' + $res.CompletionMatches[0].CompletionText | Should -BeExactly Break + } + + It 'Should complete argument with validateset attribute after comma' { + $TestString = 'function Test-ValidateSet{Param([ValidateSet("Cat","Dog")]$Param1,$Param2)};Test-ValidateSet -Param1 Dog, -Param2' + $res = TabExpansion2 -inputScript $TestString -cursorColumn ($TestString.LastIndexOf(',') + 1) + $res.CompletionMatches[0].CompletionText | Should -BeExactly Cat + } + + it 'Should complete provider dynamic parameters with quoted path' { + $Script = if ($IsWindows) + { + 'Get-ChildItem -Path "C:\" -Director' + } + else + { + 'Get-ChildItem -Path "/" -Director' + } + $res = TabExpansion2 -inputScript $Script + $res.CompletionMatches[0].CompletionText | Should -BeExactly '-Directory' + } + Context "Format cmdlet's View paramter completion" { BeforeAll { $viewDefinition = @' @@ -482,6 +752,18 @@ Describe "TabCompletion" -Tags CI { $completionText -join ' ' | Should -BeExactly 'blg csv tsv' } + it 'Should include positionally bound parameters when completing in front of parameter value' { + $TestString = 'Get-ChildItem -^ $HOME' + $CursorIndex = $TestString.IndexOf('^') + $res = TabExpansion2 -inputScript $TestString.Remove($CursorIndex, 1) -cursorColumn $CursorIndex + $res.CompletionMatches.CompletionText | Should -Contain "-Path" + } + + it 'Should complete command with an empty arrayexpression element' { + $res = TabExpansion2 -inputScript 'Get-ChildItem @()' -cursorColumn 1 + $res.CompletionMatches[0].CompletionText | Should -Be "Get-ChildItem" + } + Context "Script name completion" { BeforeAll { Setup -f 'install-powershell.ps1' -Content "" @@ -677,6 +959,26 @@ Describe "TabCompletion" -Tags CI { $expected = ($expected | Sort-Object -CaseSensitive | ForEach-Object { "./$_" }) -join ":" } + + It "PSScriptRoot path completion when AST extent has file identity" { + $scriptText = '"$PSScriptRoot\BugFix.Tests"' + $tokens = $null + $scriptAst = [System.Management.Automation.Language.Parser]::ParseInput( + $scriptText, + $PSCommandPath, + [ref] $tokens, + [ref] $null) + + $cursorPosition = $scriptAst.Extent.StartScriptPosition. + GetType(). + GetMethod('CloneWithNewOffset', [System.Reflection.BindingFlags]'NonPublic, Instance'). + Invoke($scriptAst.Extent.StartScriptPosition, @($scriptText.Length - 1)) + + $res = TabExpansion2 -ast $scriptAst -tokens $tokens -positionOfCursor $cursorPosition + $res.CompletionMatches | Should -HaveCount 1 + $expectedPath = Join-Path $PSScriptRoot -ChildPath BugFix.Tests.ps1 + $res.CompletionMatches[0].CompletionText | Should -Be "`"$expectedPath`"" + } } Context "Cmdlet name completion" { @@ -732,7 +1034,7 @@ Describe "TabCompletion" -Tags CI { @{ inputStr = '[math].G'; expected = 'GenericParameterAttributes'; setup = $null } @{ inputStr = '[Environment+specialfolder]::App'; expected = 'ApplicationData'; setup = $null } @{ inputStr = 'icm {get-pro'; expected = 'Get-Process'; setup = $null } - @{ inputStr = 'write-ouput (get-pro'; expected = 'Get-Process'; setup = $null } + @{ inputStr = 'write-output (get-pro'; expected = 'Get-Process'; setup = $null } @{ inputStr = 'iex "get-pro'; expected = '"Get-Process"'; setup = $null } @{ inputStr = '$variab'; expected = '$variableA'; setup = { $variableB = 2; $variableA = 1 } } @{ inputStr = 'a -'; expected = '-keys'; setup = { function a {param($keys) $a} } } @@ -818,7 +1120,7 @@ Describe "TabCompletion" -Tags CI { @{ inputStr = '[System.Management.Automation.Runspaces.runspacef'; expected = 'System.Management.Automation.Runspaces.RunspaceFactory'; setup = $null } @{ inputStr = '[specialfol'; expected = 'System.Environment+SpecialFolder'; setup = $null } ## tab completion for variable names in '{}' - @{ inputStr = '${PSDefault'; expected = '$PSDefaultParameterValues'; setup = $null } + @{ inputStr = '${PSDefault'; expected = '${PSDefaultParameterValues}'; setup = $null } ) } @@ -840,6 +1142,16 @@ Describe "TabCompletion" -Tags CI { $res.CompletionMatches[0].CompletionText | Should -BeExactly $afterTab } + It "Tab completion UNC path with forward slashes" -Skip:(!$IsWindows) { + $beforeTab = "//localhost/admin" + # it is expected that tab completion turns forward slashes into backslashes + $afterTab = "\\localhost\ADMIN$" + $res = TabExpansion2 -inputScript $beforeTab -cursorColumn $beforeTab.Length + $res.CompletionMatches.Count | Should -BeGreaterThan 0 + $res.CompletionMatches[0].CompletionText | Should -BeExactly $afterTab + } + + It "Tab completion for registry" -Skip:(!$IsWindows) { $beforeTab = 'registry::HKEY_l' $afterTab = 'registry::HKEY_LOCAL_MACHINE' @@ -941,6 +1253,21 @@ Describe "TabCompletion" -Tags CI { $res.CompletionMatches[1].CompletionText | Should -BeExactly 'dog' } + It "Tab completion for validateSet attribute takes precedence over enums" { + function foo { param([ValidateSet('DarkBlue','DarkCyan')][ConsoleColor]$p) } + $inputStr = "foo " + $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length + $res.CompletionMatches | Should -HaveCount 2 + $res.CompletionMatches[0].CompletionText | Should -BeExactly 'DarkBlue' + $res.CompletionMatches[1].CompletionText | Should -BeExactly 'DarkCyan' + } + + It "Tab completion for attribute type" { + $inputStr = '[validateset()]$var1' + $res = TabExpansion2 -inputScript $inputStr -cursorColumn 2 + $res.CompletionMatches.CompletionText | Should -Contain 'ValidateSet' + } + It "Tab completion for ArgumentCompleter when AST is passed to CompleteInput" { $scriptBl = { function Test-Completion { @@ -981,6 +1308,46 @@ Describe "TabCompletion" -Tags CI { $res.CompletionMatches[1].CompletionText | Should -BeExactly 'dog' } + It "Tab completion for enum members after colon with space" -TestCases @( + @{ Space = 0 } + @{ Space = 1 } + ) { + param ($Space) + $inputStr = "Get-Command -Type:$(' ' * $Space)Al" + $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length + $res.CompletionMatches | Should -HaveCount 2 + $res.CompletionMatches[0].CompletionText | Should -BeExactly 'Alias' + $res.CompletionMatches[1].CompletionText | Should -BeExactly 'All' + } + + It "Tab completion for enum members between colon with space and space with value" -TestCases @( + @{ LeftSpace = 0; RightSpace = 0 } + @{ LeftSpace = 0; RightSpace = 1 } + @{ LeftSpace = 1; RightSpace = 0 } + @{ LeftSpace = 1; RightSpace = 1 } + ) { + param ($LeftSpace, $RightSpace) + $inputStrEndsWithCursor = "Get-Command -Type:$(' ' * $LeftSpace)" + $inputStr = $inputStrEndsWithCursor + "$(' ' * $RightSpace)Alias" + $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStrEndsWithCursor.Length + $expectedArray = [enum]::GetNames([System.Management.Automation.CommandTypes]) | Sort-Object + $res.CompletionMatches.CompletionText | Should -Be $expectedArray + } + + It "Tab completion for enum members between comma with space and space with parameter" -TestCases @( + @{ LeftSpace = 0; RightSpace = 0 } + @{ LeftSpace = 0; RightSpace = 1 } + @{ LeftSpace = 1; RightSpace = 0 } + @{ LeftSpace = 1; RightSpace = 1 } + ) { + param ($LeftSpace, $RightSpace) + $inputStrEndsWithCursor = "Get-Command -Type Alias,$(' ' * $LeftSpace)" + $inputStr = $inputStrEndsWithCursor + "$(' ' * $RightSpace)-All" + $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStrEndsWithCursor.Length + $expectedArray = [enum]::GetNames([System.Management.Automation.CommandTypes]) | Sort-Object + $res.CompletionMatches.CompletionText | Should -Be $expectedArray + } + It "Tab completion for enum members after comma" { $inputStr = "Get-Command -Type Alias,c" $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length @@ -1092,11 +1459,51 @@ Describe "TabCompletion" -Tags CI { It "Test Attribute member completion multiple members" { $inputStr = "function bar { [parameter(Position,]param() }" $res = TabExpansion2 -inputScript $inputStr -cursorColumn ($inputStr.IndexOf(',') + 1) - $res.CompletionMatches | Should -HaveCount 10 + $res.CompletionMatches | Should -HaveCount 9 $entry = $res.CompletionMatches | Where-Object CompletionText -EQ "Mandatory" $entry.CompletionText | Should -BeExactly "Mandatory" } + It "Test Attribute scriptblock completion" { + $inputStr = '[ValidateScript({Get-Child})]$Test=ls' + $res = TabExpansion2 -inputScript $inputStr -cursorColumn ($inputStr.IndexOf('}')) + $res.CompletionMatches | Should -HaveCount 1 + $entry = $res.CompletionMatches | Where-Object CompletionText -EQ "Get-ChildItem" + $entry.CompletionText | Should -BeExactly "Get-ChildItem" + } + + It '' -TestCases @( + @{ + Intent = 'Complete attribute members on empty line' + Expected = @('Position','ParameterSetName','Mandatory','ValueFromPipeline','ValueFromPipelineByPropertyName','ValueFromRemainingArguments','HelpMessage','HelpMessageBaseName','HelpMessageResourceId','DontShow') + TestString = @' +function bar { [parameter( + + +^ + + )]param() } +'@ + } + @{ + Intent = 'Complete attribute members on empty line with preceding member' + Expected = @('Position','ParameterSetName','Mandatory','ValueFromPipeline','ValueFromPipelineByPropertyName','ValueFromRemainingArguments','HelpMessage','HelpMessageBaseName','HelpMessageResourceId','DontShow') + TestString = @' +function bar { [parameter( +Mandatory, + +^ + + )]param() } +'@ + } + ){ + param($Expected, $TestString) + $CursorIndex = $TestString.IndexOf('^') + $res = TabExpansion2 -cursorColumn $CursorIndex -inputScript $TestString.Remove($CursorIndex, 1) + $res.CompletionMatches[0].CompletionText | Should -BeIn $Expected + } + It "Test completion with line continuation" { $inputStr = @' dir -Recurse ` @@ -1134,6 +1541,90 @@ dir -Recurse ` $res.CompletionMatches | Should -HaveCount 4 [string]::Join(',', ($res.CompletionMatches.completiontext | Sort-Object)) | Should -BeExactly "-Path,-PipelineVariable,-PSPath,-pv" } + + It "Test completion for HttpVersion parameter name" { + $inputStr = 'Invoke-WebRequest -HttpV' + $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length + $res.CompletionMatches | Should -HaveCount 1 + $res.CompletionMatches[0].CompletionText | Should -BeExactly "-HttpVersion" + } + + It "Test completion for HttpVersion parameter" { + $inputStr = 'Invoke-WebRequest -HttpVersion ' + $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length + $res.CompletionMatches | Should -HaveCount 4 + [string]::Join(',', ($res.CompletionMatches.completiontext | Sort-Object)) | Should -BeExactly "1.0,1.1,2.0,3.0" + } + + It "Test completion for HttpVersion parameter with input" { + $inputStr = 'Invoke-WebRequest -HttpVersion 1' + $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length + $res.CompletionMatches | Should -HaveCount 2 + [string]::Join(',', ($res.CompletionMatches.completiontext | Sort-Object)) | Should -BeExactly "1.0,1.1" + } + + It 'Should complete Select-Object properties without duplicates' { + $res = TabExpansion2 -inputScript '$PSVersionTable | Select-Object -Property Count,' + $res.CompletionMatches.CompletionText | Should -Not -Contain "Count" + } + + It '' -TestCases @( + @{ + Intent = 'Complete loop labels with no input' + Expected = 'Outer','Inner' + TestString = ':Outer while ($true){:Inner while ($true){ break ^ }}' + } + @{ + Intent = 'Complete loop labels that are accessible' + Expected = 'Outer' + TestString = ':Outer do {:Inner while ($true){ break } continue ^ } until ($false)' + } + @{ + Intent = 'Complete loop labels with partial input' + Expected = 'Outer' + TestString = ':Outer do {:Inner while ($true){ break } continue o^ut } while ($true)' + } + @{ + Intent = 'Complete loop label for incomplete switch' + Expected = 'Outer' + TestString = ':Outer switch ($x){"randomValue"{ continue ^' + } + @{ + Intent = 'Complete loop label for incomplete do loop' + Expected = 'Outer' + TestString = ':Outer do {:Inner while ($true){ break } continue ^' + } + @{ + Intent = 'Complete loop label for incomplete for loop' + Expected = 'forLoop' + TestString = ':forLoop for ($i = 0; $i -lt $SomeCollection.Count; $i++) {continue ^' + } + @{ + Intent = 'Complete loop label for incomplete while loop' + Expected = 'WhileLoop' + TestString = ':WhileLoop while ($true){ break ^' + } + @{ + Intent = 'Complete loop label for incomplete foreach loop' + Expected = 'foreachLoop' + TestString = ':foreachLoop foreach ($x in $y) { break ^' + } + @{ + Intent = 'Not Complete loop labels with colon' + Expected = $null + TestString = ':Outer foreach ($x in $y){:Inner for ($i = 0; $i -lt $X.Count; $i++){ break :O^}}' + } + @{ + Intent = 'Not Complete loop labels if cursor is in front of existing label' + Expected = $null + TestString = ':Outer switch ($x){"Value1"{break ^ Outer}}' + } + ){ + param($Expected, $TestString) + $CursorIndex = $TestString.IndexOf('^') + $res = TabExpansion2 -cursorColumn $CursorIndex -inputScript $TestString.Remove($CursorIndex, 1) + $res.CompletionMatches.CompletionText | Should -BeExactly $Expected + } } Context "Module completion for 'using module'" { @@ -1203,6 +1694,21 @@ dir -Recurse ` $res.CompletionMatches[0].CompletionText | Should -BeExactly $expected } + It "Tab completion for file array element between comma with space and space with parameter" -TestCases @( + @{ LeftSpace = 0; RightSpace = 0 } + @{ LeftSpace = 0; RightSpace = 1 } + @{ LeftSpace = 1; RightSpace = 0 } + @{ LeftSpace = 1; RightSpace = 1 } + ) { + param ($LeftSpace, $RightSpace) + $inputStrEndsWithCursor = "dir .\commaA.txt,$(' ' * $LeftSpace)" + $inputStr = $inputStrEndsWithCursor + "$(' ' * $RightSpace)-File" + $expected = ".${separator}commaA.txt" + $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStrEndsWithCursor.Length + $res.CompletionMatches | Should -HaveCount 1 + $res.CompletionMatches[0].CompletionText | Should -BeExactly $expected + } + It "Test comma with Enum array element" { $inputStr = "gcm -CommandType Cmdlet," $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length @@ -1333,6 +1839,13 @@ dir -Recurse ` $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length $res.CompletionMatches | Should -BeNullOrEmpty } + + It "A single dash should not complete to anything" { + function test-{} + $inputStr = 'git -' + $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length + $res.CompletionMatches | Should -BeNullOrEmpty + } } Context "Tab completion error tests" { @@ -1428,6 +1941,18 @@ dir -Recurse ` @{ inputStr = '[Microsoft.Management.Infrastructure.CimClass]$c = $null; $c.CimClassNam'; expected = 'CimClassName' } @{ inputStr = '[Microsoft.Management.Infrastructure.CimClass]$c = $null; $c.CimClassName.Substrin'; expected = 'Substring(' } @{ inputStr = 'Get-CimInstance -ClassName Win32_Process | %{ $_.ExecutableP'; expected = 'ExecutablePath' } + @{ inputStr = 'Get-CimInstance -ClassName Win32_Process | Invoke-CimMethod -MethodName SetPriority -Arguments @{'; expected = 'Priority' } + @{ inputStr = 'Get-CimInstance -ClassName Win32_Service | Invoke-CimMethod -MethodName Change -Arguments @{d'; expected = 'DesktopInteract' } + @{ inputStr = 'Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{'; expected = 'CommandLine' } + @{ inputStr = 'New-CimInstance Win32_Environment -Property @{'; expected = 'Caption' } + @{ inputStr = 'Get-CimInstance Win32_Environment | Set-CimInstance -Property @{'; expected = 'Name' } + @{ inputStr = 'Set-CimInstance -Namespace root/CIMV'; expected = 'root/CIMV2' } + @{ inputStr = 'Get-CimInstance Win32_Process -Property '; expected = 'Caption' } + @{ inputStr = 'Get-CimInstance Win32_Process -Property Caption,'; expected = 'Description' } + ) + $FailCases = @( + @{ inputStr = "Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments " } + @{ inputStr = "New-CimInstance Win32_Process -Property " } ) } @@ -1438,6 +1963,13 @@ dir -Recurse ` $res.CompletionMatches.Count | Should -BeGreaterThan 0 $res.CompletionMatches[0].CompletionText | Should -Be $expected } + + It "CIM cmdlet input '' should not successfully complete" -TestCases $FailCases -Skip:(!$IsWindows) { + param($inputStr) + + $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length + $res.CompletionMatches[0].ResultType | should -Not -Be 'Property' + } } Context "Module cmdlet completion tests" { @@ -1656,6 +2188,30 @@ function MyFunction ($param1, $param2) { } } +'@ + } + @{ + Intent = 'Complete help keyword PARAMETER argument for function inside advanced function' + Expected = 'param1','param2' + TestString = @' +function Verb-Noun +{ + Param + ( + [Parameter()] + [string[]] + $ParamA + ) + Begin + { + <# + .Parameter ^ + #> + function MyFunction ($param1, $param2) + { + } + } +} '@ } @{ diff --git a/test/powershell/Language/Interop/DotNet/DotNetInterop.Tests.ps1 b/test/powershell/Language/Interop/DotNet/DotNetInterop.Tests.ps1 index 02d49c4435c..42426b8279a 100644 --- a/test/powershell/Language/Interop/DotNet/DotNetInterop.Tests.ps1 +++ b/test/powershell/Language/Interop/DotNet/DotNetInterop.Tests.ps1 @@ -121,7 +121,7 @@ namespace DotNetInterop } It "Calling constructor of a ByRef-like type via dotnet adapter should fail gracefully - " -TestCases @( - @{ Number = 1; Script = { [System.Span[string]]::new.Invoke("abc") } } + @{ Number = 1; Script = { [System.Span[string]]::new.Invoke([ref]$null) } } @{ Number = 2; Script = { [DotNetInterop.MyByRefLikeType]::new.Invoke(2) } } ) { param($Script) diff --git a/test/powershell/Language/Operators/PipelineChainOperator.Tests.ps1 b/test/powershell/Language/Operators/PipelineChainOperator.Tests.ps1 index e2e54c32170..15340dd18e6 100644 --- a/test/powershell/Language/Operators/PipelineChainOperator.Tests.ps1 +++ b/test/powershell/Language/Operators/PipelineChainOperator.Tests.ps1 @@ -47,7 +47,7 @@ Describe "Experimental Feature: && and || operators - Feature-Enabled" -Tag CI { @{ Statement = 'testexe -returncode -1 || testexe -returncode -2 && testexe -echoargs "A"'; Output = @('-1', '-2') } @{ Statement = 'testexe -returncode -1 || testexe -returncode -2 || testexe -echoargs "B"'; Output = @('-1', '-2', 'Arg 0 is ') } - # Native command and succesful cmdlet + # Native command and successful cmdlet @{ Statement = 'Test-SuccessfulCommand && testexe -returncode 0'; Output = @('SUCCESS', '0') } @{ Statement = 'testexe -returncode 0 && Test-SuccessfulCommand'; Output = @('0', 'SUCCESS') } @{ Statement = 'Test-SuccessfulCommand && testexe -returncode 1'; Output = @('SUCCESS', '1') } diff --git a/test/powershell/Language/Parser/BNotOperator.Tests.ps1 b/test/powershell/Language/Parser/BNotOperator.Tests.ps1 index eb6dd934e30..41930766dc6 100644 --- a/test/powershell/Language/Parser/BNotOperator.Tests.ps1 +++ b/test/powershell/Language/Parser/BNotOperator.Tests.ps1 @@ -12,7 +12,7 @@ $ns = [Guid]::NewGuid() -replace '-','' $typeDefinition = "namespace ns_$ns`n{" -$enumTypeNames = foreach ($baseType in $baseTypes.Keys) +foreach ($baseType in $baseTypes.Keys) { $baseTypeName = $baseTypes[$baseType] $typeDefinition += @" @@ -24,14 +24,12 @@ $enumTypeNames = foreach ($baseType in $baseTypes.Keys) Max = $($baseType::MaxValue) } "@ - - "ns_$ns.E_$baseTypeName" } $typeDefinition += "`n}" -Write-Verbose $typeDefinition -Add-Type $typeDefinition +Write-Verbose $typeDefinition -verbose +$enumTypeNames = Add-Type $typeDefinition -Pass Describe "bnot on enums" -Tags "CI" { foreach ($enumType in [type[]]$enumTypeNames) diff --git a/test/powershell/Language/Parser/Conversions.Tests.ps1 b/test/powershell/Language/Parser/Conversions.Tests.ps1 index bfeeddc4f1a..119f74d9a8a 100644 --- a/test/powershell/Language/Parser/Conversions.Tests.ps1 +++ b/test/powershell/Language/Parser/Conversions.Tests.ps1 @@ -526,7 +526,7 @@ Describe 'method conversion' -Tags 'CI' { } Describe 'float/double precision when converting to string' -Tags "CI" { - It "-to-[string] conversion in PowerShell should use the precision specifier " -TestCases @( + It "-to-[string] conversion in PowerShell should use the precision specifier ()" -TestCases @( @{ SourceType = [double]; Format = "G15"; ValueScript = { 1.1 * 3 }; StringConversionResult = "3.3"; ToStringResult = "3.3000000000000003" } @{ SourceType = [double]; Format = "G15"; ValueScript = { 1.1 * 6 }; StringConversionResult = "6.6"; ToStringResult = "6.6000000000000005" } @{ SourceType = [double]; Format = "G15"; ValueScript = { [System.Math]::E }; StringConversionResult = [System.Math]::E.ToString("G15"); ToStringResult = [System.Math]::E.ToString() } diff --git a/test/powershell/Language/Parser/MethodInvocation.Tests.ps1 b/test/powershell/Language/Parser/MethodInvocation.Tests.ps1 index f28b6801389..ca4db39bd66 100644 --- a/test/powershell/Language/Parser/MethodInvocation.Tests.ps1 +++ b/test/powershell/Language/Parser/MethodInvocation.Tests.ps1 @@ -1,10 +1,270 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -if ( $IsCoreCLR ) { - return + +Describe 'Generic Method invocation' -Tags 'CI' { + + BeforeAll { + $EmptyArrayCases = @( + @{ + Script = '[Array]::Empty[string]()' + ExpectedType = [string[]] + } + @{ + Script = '[Array]::Empty[System.Collections.Generic.Dictionary[System.Numerics.BigInteger, System.Collections.Generic.List[string[,]]]]()' + ExpectedType = [System.Collections.Generic.Dictionary[System.Numerics.BigInteger, System.Collections.Generic.List[string[, ]]][]] + } + ) + + $IndexingAProperty = @( + @{ + Script = '[object]::Property[[type]]' + IndexType = 'System.Management.Automation.Language.TypeExpressionAst' + IndexString = '[type]' + } + @{ + Script = '$object.IPSubnet[[Array]::IndexOf($_.IPAddress, $_.IPAddress[0])]' + IndexType = 'System.Management.Automation.Language.InvokeMemberExpressionAst' + IndexString = '[Array]::IndexOf($_.IPAddress, $_.IPAddress[0])' + } + @{ + Script = @' + [IPAddress]::Parse( + $_.IPSubnet[ + [Array]::IndexOf($_.IPAddress, $_.IPAddress[0]) + ] + ) +'@ + IndexType = 'System.Management.Automation.Language.InvokeMemberExpressionAst' + IndexString = '[Array]::IndexOf($_.IPAddress, $_.IPAddress[0])' + } + @{ + Script = @' + [IPAddress]::Parse( + $_.IPSubnet[ + ([Array]::IndexOf($_.IPAddress, $_.IPAddress[0])) + ] + ) +'@ + IndexType = 'System.Management.Automation.Language.ParenExpressionAst' + IndexString = '([Array]::IndexOf($_.IPAddress, $_.IPAddress[0]))' + } + ) + + $ExpectedParseErrors = @( + @{ + Script = '$object.Method[incompl' + ExpectedErrors = @('EndSquareBracketExpectedAtEndOfType') + ErrorCount = 1 + } + @{ + Script = '[type]::Member[incompl' + ExpectedErrors = @('EndSquareBracketExpectedAtEndOfType') + ErrorCount = 1 + } + @{ + Script = '$object.Method[Type1[Type2' + ExpectedErrors = @('EndSquareBracketExpectedAtEndOfAttribute','EndSquareBracketExpectedAtEndOfType') + ErrorCount = 2 + } + @{ + Script = '[array]::empty[type]]()' + ExpectedErrors = @('MissingArrayIndexExpression', 'UnexpectedToken', 'ExpectedExpression') + ErrorCount = 3 + } + @{ + Script = '$object.Method[type,]()' + ExpectedErrors = @('MissingTypename') + ErrorCount = 1 + } + @{ + Script = '$object.Method[]()' + ExpectedErrors = @('MissingArrayIndexExpression', 'UnexpectedToken', 'ExpectedExpression') + ErrorCount = 3 + } + @{ + Script = '$object.Method[,]()' + ExpectedErrors = @('MissingExpressionAfterOperator', 'UnexpectedToken', 'ExpectedExpression') + ErrorCount = 3 + } + @{ + Script = '$object.Method[,type]()' + ExpectedErrors = @('MissingExpressionAfterOperator', 'UnexpectedToken', 'ExpectedExpression') + ErrorCount = 3 + } + @{ + Script = '$object.Method[type()' + ExpectedErrors = @('EndSquareBracketExpectedAtEndOfType', 'UnexpectedToken', 'ExpectedExpression') + ErrorCount = 3 + } + @{ + Script = '$object.Method[type)' + ExpectedErrors = @('EndSquareBracketExpectedAtEndOfType', 'UnexpectedToken') + ErrorCount = 2 + } + @{ + Script = '$object.Method[[type]]()' + ExpectedErrors = @('UnexpectedToken', 'ExpectedExpression') + ErrorCount = 2 + } + @{ + Script = '[Array]::Empty[[type]]()' + ExpectedErrors = @('UnexpectedToken', 'ExpectedExpression') + ErrorCount = 2 + } + @{ + Script = '$object.Property[type]' + ExpectedErrors = @('MissingArrayIndexExpression', 'UnexpectedToken') + ErrorCount = 2 + } + ) + } + + It 'does not throw a parse error for "