-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Fix TabExpansion2 variable leak when completing variables #18763
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix TabExpansion2 variable leak when completing variables #18763
Conversation
|
@MartinGC94 - PSReadline specific code would need to be pushed to that repo as it can then be used not only in v7 but also v5.1 too |
| break; | ||
| } | ||
| scopeToRestore = context.EngineSessionState.CurrentScope; | ||
| context.EngineSessionState.CurrentScope = scopeToRestore.Parent; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If TabExpansion2 is defined in a module then this will revert to the module's script scope rather than the actual previous scope.
Also if TabExpansion2 is dot sourced from global, Parent will be null here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If
TabExpansion2is defined in a module then this will revert to the module'sscriptscope rather than the actual previous scope.
What's the difference? If I look at the session state API it seems to simply use the Parent property to move to different scopes:
https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/SessionStateScopeAPIs.cs#L128
https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/SessionStateScopeEnumerator.cs#L30
https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/SessionStateVariableAPIs.cs#L1791
Can you show a simple example where it would cause a problem?
Also if
TabExpansion2is dot sourced from global,Parentwill be null here
How do you dot source from global? Do you mean like this: . $Function:TabExpansion2 "$" because if so it won't use the parent because the command name isn't "TabExpansion2".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the difference? If I look at the session state API it seems to simply use the Parent property to move to different scopes:
A SessionState is sort of like a Stack of scopes. Every module has its own stack. To move to a different stack, EngineSessionState needs to be assigned. Though, afaik the previous SessionState is only stored in a local variable inside a few differenttry/finallys.
How do you dot source from global? Do you mean like this:
. $Function:TabExpansion2 "$"because if so it won't use the parent because the command name isn't "TabExpansion2".
You'd do . TabExpansion2 'etc', or the more likely route would be calling PowerShell.AddCommand("TabExpansion2", useLocalScope: false) from a host application.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All right, thanks for the explanation. I've added a null check for the dot sourcing scenario.
As for the module scope, would it be any different from the current behavior? If you are tab completing a variable today it's just calling Get-Item variable:\* from the tabexpansion2 scope which as far as I can tell retrieves variables from that scope and traverses through the Parent property on each scope. So with my change the only difference would be that it skips the initial TabExpansion2 scope, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All right, thanks for the explanation. I've added a null check for the dot sourcing scenario.
So null is one potential dot sourcing problem. Lets say the debugger is stopped in the middle of a script and the REPL starts. If TabExpansion2 is dot sourced then we'd be reverting not just TabExpansion2 but the scope of the currently running command as well. Now, I can't say for sure if that would be a problem, but some very bizarre bugs are possible when changing to an unexpected scope.
Ideally we'd be able to detect that TabExpansion2 was dot sourced and abort the attempt to revert scope. I don't know of a reliable way to tell that from here though, @daxian-dbw may have some ideas
As for the module scope, would it be any different from the current behavior?
Mmmm that's a fair point. Though maybe the behavior should be to skip trying to "fix" the issue if EngineSessionState is not TopLevelSessionState. I can't come up with a specific scenario, but changing scope when a module is involved worries me, and even the fix doesn't necessarily help the scenario.
Although at the same time, if TabExpansion2 is defined with no session state affinity, and lets say the debugger is stopped inside module code, then we'd want to revert still.
Maybe the check should be if (commandInfo.ScriptBlock.SesssionStateInternal != null) { DontRevert }?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally we'd be able to detect that TabExpansion2 was dot sourced and abort the attempt to revert scope. I don't know of a reliable way to tell that from here though
I think you do without realizing. There's a UseLocalScope property in the CurrentCommandProcessor and you said that PowerShell.AddCommand("TabExpansion2", useLocalScope: false) is equivalent to dot sourcing, so can't we just use that? It seems to toggle back and forth like I would expect when I dotsource or run it normally.
Alternatively, instead of changing the scope we could just add the parent scope as an additional property to the CompletionContext object and update the variable/member completion code to use the parent scope if it's available. I don't think any other completion paths is affected by the session state but I could be wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you do without realizing. There's a
UseLocalScopeproperty in theCurrentCommandProcessorand you said thatPowerShell.AddCommand("TabExpansion2", useLocalScope: false)is equivalent to dot sourcing, so can't we just use that?
Oh yay I was wrong! 😁 I must have been thinking of when you only have the call stack, good catch!
|
This pull request has been automatically marked as Review Needed because it has been there has not been any activity for 7 days. |
|
@MartinGC94 I would need signoff from either @daxian-dbw or @SeeminglyScience before this can be merged, as the as SMEs. |
|
This pull request has been automatically marked as Review Needed because it has been there has not been any activity for 7 days. |
|
This PR has Quantification details
Why proper sizing of changes matters
Optimal pull request sizes drive a better predictable PR flow as they strike a
What can I do to optimize my changes
How to interpret the change counts in git diff output
Was this comment helpful? 👍 :ok_hand: :thumbsdown: (Email) |
|
FYI I haven't forgotten about this, I need some time to play around with it and see if any issues crop up |
JamesWTruher
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks fine, it seems that the automatic variables which are created by the use of [CmdletBinding()] for TabExpansion2 should also be removed from completion, yes?
You mean variables like |
SeeminglyScience
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't find a way to break this so LGTM! Thanks again @MartinGC94! ❤️
|
This also fixes the following scenario: It didn't work before because the variable |
|
🎉 Handy links: |
PR Summary
Fixes PowerShell/PowerShellEditorServices#1969 where tab completing variables would show Variables/Parameters from TabExpansion2.
This approach could probably be extended to fix a similar issue with
PSConsoleHostReadLine(see #16941 ) but I'm not sure if I can add PSReadLine specific code here.PR Context
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerWIP:or[ WIP ]to the beginning of the title (theWIPbot will keep its status check atPendingwhile the prefix is present) and remove the prefix when the PR is ready.(which runs in a different PS Host).