- Notifications
You must be signed in to change notification settings - Fork8.1k
Fix Propertyless Object not displaying correctly after ETS introspection#25871
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:master
Are you sure you want to change the base?
Conversation
GregoireLD commentedAug 16, 2025
I have sole ownership of intellectual property rights to my Submissions and I am not making Submissions in the course of work for my employer. @microsoft-github-policy-service agree |
GregoireLD commentedAug 16, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I found that a Pester test existed specifically to check the ToString() fallback path, so I added a check to ensure the fallback now also still works after introspecting the object. |
GregoireLD commentedAug 29, 2025
I got notified about the auto-“Review Needed”, so just in case, I wanted to confirm this is ready for reviewing. |
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.
Pull Request Overview
This PR fixes a bug where propertyless objects (objects with only hidden properties) were not displaying correctly after being inspected withGet-Member or similar introspection commands. The fix replaces the introspection-sensitiveHasNonRemotingProperties() method with a newIsPropertyLessObject() method that properly handles objects after ETS introspection.
Key changes:
- Introduced new
IsPropertyLessObject()method that usesAssociationManager.ExpandAll()to evaluate properties without being affected by introspection side effects - Removed the deprecated
HasNonRemotingProperties()method and redundant property checks - Added
RemotingConstants.EventObjectto the list of remoting properties to filter - Added comprehensive test coverage for the bug fix scenario
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs | Implements newIsPropertyLessObject() method and removes old logic; adds EventObject to remoting property filter |
src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs | Updates to use newIsPropertyLessObject() method instead ofHasNonRemotingProperties() |
test/powershell/engine/Formatting/BugFix.Tests.ps1 | Adds test case verifying propertyless objects display correctly afterGet-Member introspection |
💡Add Copilot custom instructions for smarter, more guided reviews.Learn how to get started.
src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
| { | ||
| internalstaticboolIsPropertyLessObject(PSObjectso) | ||
| { | ||
| List<MshResolvedExpressionParameterAssociation>allProperties=AssociationManager.ExpandAll(so); |
CopilotAINov 20, 2025
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.
The new implementation callsAssociationManager.ExpandAll(so) which expands and collects all properties into a list upfront. The previousGetFirstPropertyOrDefault approach could short-circuit as soon as it found the first non-remoting property. For objects with many properties, this could cause a noticeable performance regression since all properties must now be expanded before any can be checked.
However, this approach does fix the correctness issue where ETS introspection was causing problems. The performance trade-off may be acceptable given that this is fixing a bug, but it's worth considering if there's a way to achieve both correctness and early-exit performance optimization.
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.
It is interesting suggestion. I'd prefer avoid using ExpandAll.
@GregoireLD Have you considered using a method from a closure/tear off class for the delegate implementing fast return?
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.
@iSazonov No I must admit I have not. As a non-expert in C#, I tried to avoid introducing other unseen errors myself, I'm afraid using a closure would've potentially exposed me to the same kind of error we got because of theMemberNamePredicate. As such I preferred to lean on the safe side, and root my solution into what was done beforeGetFirstPropertyOrDefault got introduced here.
Yet, to avoid too much of a speed penalty, the way I look through the properties is that it will break from the list as soon as possible: Right at the start if there is no properties, or as soon as a non-remoting property is found.
So even with a massive object, and while I'm not sure how to optimise theExpandAll section, the looking through part can't, in the worst case scenario, read more than 6 properties at most (the maximum number of RemotingProperties there is, plus one).
Also, this code only gets used when handling objects where two specific conditions are met: there is no view attached to said object, and it's not a scalar either. Only then it will test if there is visible properties or not.
If you have a suggestion I can try and look into it.
As a sidetone, if this doesn't introduce a speed penalty, I would suggest to keep this PR, aimed at correcting the propertyless bug, while I work on a separate one looking into optimizing edge-cases uses of theExpandAll 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.
@iSazonov After a quick benchmark, comparing the public PowerShell build and mine, my version usually gets similar speeds, or slightly better: I know this is anecdotal evidence, but based on my computer, outputting a "small" object 50000 times takes around 720 ms on the public build, and 660 ms on my version. For a "Big" (15 properties, including nested objects) object, 1050 ms on the public build, and 940 ms on my version.
When searching around though, I found PR#8785, the PR which introduced the change (and the bug) for performance reasons. The test included in this PR did put enough of a strain on my version to see a noticeable difference, similar to the speed we had prior to PR#8785 optimizations.
As is, this version would usually be similar ins speed to current version PowerShell, and in the worst case scenario, similar to the previous "pre PR#8785" version.
Thanks to your suggestion, I'm also exploring an alternative approach on a separate branch using aso.Properties.GetEnumerator() for a truly lazy property enumeration. And, so far, the speed gain is quite noticeable: instead of a 9x "pre PR#8785" penalty, the worst I got was a 3x penalty (again, only under the most straining tests). Yet, as this would be a new architectural pattern not matching either the current or original implementations, I think it might be preferable to address it in a separate PR.
What do you think ?
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.
Current proposed code de-facto reverts#8785. I cannot accept this.
If it was a workaround in#8785 to get fast check of absent of properties, we should find better way than enumerate all properties.
In debugger I seeIsNotRemotingProperty() passpstypenames and as result we see the issue. Perhaps we should think in the direction - is it right to use _mshOwner.InstanceMembers and what we should skip.
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.
@iSazonov Here, I merged my Lazy evaluation method in here so you can have a look at it. In most cases it was faster than current method, and the worst cases stays much faster than Pre#8785 speeds (instead of a 9x speed gain, we still gain a 2x or 3x compared to Pre#8785 speeds)
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 using enumerator force Engine to evaluate meta data for properties. Engine will cache the data and this give still a perf win. I think it make sense to keep#8785 achievements.
I have no full understanding that is right way to fix the issue but you could try to filter outpstypenames as simplest fix that I can see in the time.
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'm a bit worried that specifically filtering outpstypenames might lead into a similar issue down the line if some other properties were introduced later. While we do leave out some performance gains on the table (but we still benefits from some of#8785 optimizations), I think relying on a more "robust" or standardized mechanism like the GetEnumerator would make it more of a future-proof solution, if that makes sense to you
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.
The issue is an edge case. So it makes no sense to revert#8785 in some way and slow down hot path. If we could find simple fix the PR could be approved.
GregoireLDNov 24, 2025 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I understand, one simpler fix I found would be to repurpose the currentIsNotRemotingProperty to also include and filter out thePSObject.PSTypeNames property. I will push a separate PR for you to compare the two.
EDIT: I created a separate PR (PR#26520) with said, much simpler, approach
src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
...werShell.Commands.Utility/commands/utility/FormatAndOutput/OutGridView/OutGridViewCommand.cs OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/System.Management.Automation/FormatAndOutput/common/FormatViewManager.cs OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
…ViewManager.csGrammatical fixCo-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ViewManager.csCapitalization fixCo-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…matAndOutput/OutGridView/OutGridViewCommand.csCo-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Uses a GetEnumerator instead of ExpandAll to mitigate speed loss
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.
.canvas - Copy (2).html
.canvas - Copy (3).html
.canvas - Copy (4).html
.canvas - Copy (5).html
.canvas - Copy.html
.canvas.html
.env.txt
2025-11-22_03-26-01_f(1) - Copy.txt
2025-11-22_03-26-01_f(1).txt
2025-11-22_03-26-01_f(2) - Copy.txt
2025-11-22_03-26-01_f(3) - Copy.txt
2025-11-22_03-26-01_f(4) - Copy.txt
2025-11-22_03-26-01_f(5) - Copy.txt
2025-11-22_03-26-41_index - Copy (2).html
2025-11-22_03-26-41_index - Copy (3).html
2025-11-22_03-26-41_index - Copy (4).html
2025-11-22_03-26-41_index - Copy (5).html
2025-11-22_03-26-41_index - Copy.html
2025-11-22_03-26-41_index.html
2025-11-22_03-27-12_index - Copy (2).html
2025-11-22_03-27-12_index - Copy (3).html
2025-11-22_03-27-12_index - Copy (4).html
2025-11-22_03-27-12_index - Copy (5).html
Uploading 2025-11-22_03-27-12_index - Copy.html…
Uh oh!
There was an error while loading.Please reload this page.
PR Summary
This fix the case where a Propertyless object is rendered undisplayable after being inspected using Get-Member or any similar introspection commands.
PR Context
Propertyless object are usually displayed using a fallback to their ToString() methods.
But the method currently used to evaluate the "Propertyless-ness" of an object relies on a flawed evaluation:
After inspecting the object, new properties are dynamically attached to the object, rendering it undisplayable, because no longer regarded as Propertyless.
This PR replace the current introspection-sensitive
HasNonRemotingProperties(so)withIsPropertyLessObject(so)not sensitive to passive introspection, and also taking care of excluding Remoting Properties as before.The full rational and discussion prior to making this PR can be found in my issue report :
#25832
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright header