Powershell: Unquoted arguments using the BigInteger type-suffix can cause breaking changes

Created on 18 Jan 2020  路  9Comments  路  Source: PowerShell/PowerShell

Steps to reproduce

PS D:\> New-Item 7n

Expected behavior

(and current behavior in PS 6)

    Directory: D:\

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           1/18/2020  1:24 PM              0 7n

Actual behavior

A file with an unexpected name is created.

    Directory: D:\

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           1/18/2020  1:24 PM              0 7

Environment data

Name                           Value
----                           -----
PSVersion                      7.0.0-rc.2
PSEdition                      Core
GitCommitId                    7.0.0-rc.2
OS                             Microsoft Windows 10.0.18363
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

When the other numeric types are used as an argument, PS wraps them inside a [psobject] if their numeric and string representations differ so that PS can extract the original string if needed later (e.g. if it's bound to a [string[]] parameter as with New-Item above.) Thus New-Item 7ul works as expected, but New-Item 7n does not.

I'm sorry but I don't know how/where this happens in the code though....

Mentioning @vexx32 because I believe they created the BigInt type-suffix support (thanks!).

Breaking-Change Issue-Bug Resolution-Fixed WG-Engine

Most helpful comment

When a numeric literal is sent to a command, it's wrapped in a PSObject with it's internal TokenText property set to the original string determined at parse time.

This isn't happening for big int literals, the TokenText property is null. There's a few reasons that could be happening, the check to see if the argument should be wrapped is done here:

https://github.com/PowerShell/PowerShell/blob/70d4a899310f5460700f0be8cba5a2af1d4a0305/src/System.Management.Automation/engine/parser/Compiler.cs#L4174-L4183

So one of those checks (probably the IsNumeric check since bigint's type code is Object) needs to be tweaked.

All 9 comments

That's a good point. I'm not sure _where_ this handling happens. Perhaps @SeeminglyScience could point us in the right direction?

Inferring from the fact that 7ul works correctly (which is _also_ a new addition), the fact that 7n does not seems to indicate that whatever the parameter binder(?) is looking for is likely a static set of types, or perhaps just numeric values. It probably doesn't consider BigInteger numeric since it's not a primitive but rather a struct. 馃

When a numeric literal is sent to a command, it's wrapped in a PSObject with it's internal TokenText property set to the original string determined at parse time.

This isn't happening for big int literals, the TokenText property is null. There's a few reasons that could be happening, the check to see if the argument should be wrapped is done here:

https://github.com/PowerShell/PowerShell/blob/70d4a899310f5460700f0be8cba5a2af1d4a0305/src/System.Management.Automation/engine/parser/Compiler.cs#L4174-L4183

So one of those checks (probably the IsNumeric check since bigint's type code is Object) needs to be tweaked.

Interesting, thank you! 馃槉

So... hm.. Either we'd have to add an additional piece of logic to special case for the BigInteger case... or we'd have to alter the type code information so it reads it as numeric. It _seems_ like the latter would be the better move, but that's only if that's feasible.

I'm not really sure how the type code stuff works; is that built into .NET or something handled in LanguagePrimitives? I remember seeing stuff related to it in there, but I'm not sure if that's something we can change from the PowerShell side of things? 馃

I added new condition to pass line 4177 and it is not full fix.

@iSazonov I changed it to:

if (constElement != null
    && (LanguagePrimitives.IsNumeric(LanguagePrimitives.GetTypeCode(constElement.StaticType))
    || constElement.StaticType == typeof(System.Numerics.BigInteger)))

and Write-Host 7n wrote 7n.

My tests:

Select-String -InputObject 7n -Pattern 7n
# and
New-Item 7n

Looks like we need to add another test case for functions. Cmdlets seem to work with that change, but there's something else that needs to happen, it seems...

function Test-StringLiteral {
    param([string]$Value)
    $Value
}
# ...
PS> Test-StringLiteral -Value 7n
7

This appears to still be problematic for both simple and advanced functions.

EDIT: I found another reference to the IsNumeric() method that seems relevant here:
https://github.com/PowerShell/PowerShell/blob/d58a82ad19fbfad81e85778c8b08cb1b28f58fce/src/System.Management.Automation/engine/runtime/ScriptBlockToPowerShell.cs#L649-L660

However, even with that changed similarly to the above suggestions, I'm still not seeing script functions behave correctly with BigInteger, while they will behave perfectly well with other suffixed numerals. Time to do some more digging!!

Aaaand one further update -- adding BigInteger to the array of s_numericTypes in LanguagePrimitives seems to completely sort this out, for simple & advanced functions and all! 馃帀

Will PR a thorough fix shortly! Thanks for the pointers, everyone! 馃挅 馃槉

:tada:This issue was addressed in #11634, which has now been successfully released as v7.1.0-preview.3.:tada:

Handy links:

Was this page helpful?
0 / 5 - 0 ratings