PowerShell/test/powershell/Language/Parser/Conversions.Tests.ps1

615 lines
35 KiB
PowerShell
Raw Normal View History

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
Describe 'conversion syntax' -Tags "CI" {
# these test suite covers ([<type>]<expression>).<method>() syntax.
# it mixes two purposes: casting and super-class method calls.
It 'converts array of single enum to bool' {
# This test relies on the fact that [ConsoleColor]::Black is 0 and all other values are non-zero
[bool]@([ConsoleColor]::Black) | Should -BeFalse
[bool]@([ConsoleColor]::Yellow) | Should -BeTrue
}
It 'calls virtual method non-virtually' {
([object]"abc").ToString() | Should -Be "System.String"
# generate random string to avoid JIT optimization
$r = [guid]::NewGuid().Guid
([object]($r + "a")).Equals(($r + "a")) | Should -BeFalse
}
It 'calls method on a super-type, when conversion syntax used' {
# This test relies on the fact that there are overloads (at least 2) for ToString method.
([System.Management.Automation.ActionPreference]"Stop").ToString() | Should -Be "Stop"
}
Context "Cast object[] to more narrow generic collection" {
BeforeAll {
$testCases1 = @(
## It's intentional to have 'Command' to be `{$result = ...}` and run it with `. $Command`.
## This is because `$result = & {[List[int]]@(1,2)}` will cause the resulted List to be unraveled,
## and in that case `$result` would be just an object array.
## To prevent unraveling, Command needs to be `{, [List[int]]@(1,2)}`, but then the test case title
## would become `, [List[int]]@(1,2)`, which is more confusing than `$result = [List[int]]@(1,2)`.
## This is why the current form of `$result = [List[int]]@(1,2)` is used intentionally here.
@{ Command = { $result = [Collections.Generic.List[int]]@(1) }; CollectionType = 'List`1'; ElementType = "Int32"; Elements = @(1) }
@{ Command = { $result = [Collections.Generic.List[int]]@(1, 2) }; CollectionType = 'List`1'; ElementType = "Int32"; Elements = @(1, 2) }
@{ Command = { $result = [Collections.Generic.List[int]]"4" }; CollectionType = 'List`1'; ElementType = "Int32"; Elements = @(4) }
@{ Command = { $result = [Collections.Generic.List[int]]@("4", "5") }; CollectionType = 'List`1'; ElementType = "Int32"; Elements = @(4, 5) }
@{ Command = { $result = [Collections.Generic.List[string]]@(1) }; CollectionType = 'List`1'; ElementType = "String"; Elements = @("1") }
@{ Command = { $result = [Collections.Generic.List[string]]@(1, 2) }; CollectionType = 'List`1'; ElementType = "String"; Elements = @("1", "2") }
@{ Command = { $result = [Collections.Generic.List[string]]1 }; CollectionType = 'List`1'; ElementType = "String"; Elements = @("1") }
@{ Command = { $result = [Collections.Generic.List[string]]@("4") }; CollectionType = 'List`1'; ElementType = "String"; Elements = @("4") }
@{ Command = { $result = [System.Collections.ObjectModel.Collection[int]]@(1) }; CollectionType = 'Collection`1'; ElementType = "Int32"; Elements = @(1) }
@{ Command = { $result = [System.Collections.ObjectModel.Collection[int]]@(1, 2) }; CollectionType = 'Collection`1'; ElementType = "Int32"; Elements = @(1, 2) }
@{ Command = { $result = [System.Collections.ObjectModel.Collection[int]]"4" }; CollectionType = 'Collection`1'; ElementType = "Int32"; Elements = @(4) }
@{ Command = { $result = [System.Collections.ObjectModel.Collection[int]]@("4", "5") }; CollectionType = 'Collection`1'; ElementType = "Int32"; Elements = @(4, 5) }
@{ Command = { $result = [Collections.Generic.List[System.IO.FileInfo]]@('TestFile') };
CollectionType = 'List`1'; ElementType = "FileInfo"; Elements = @('TestFile')
}
@{ Command = { $result = [Collections.Generic.List[System.IO.FileInfo]]@('TestFile1', 'TestFile2') };
CollectionType = 'List`1'; ElementType = "FileInfo"; Elements = @('TestFile1', 'TestFile2')
}
@{ Command = { $result = [Collections.Generic.List[System.IO.FileInfo]]'TestFile' };
CollectionType = 'List`1'; ElementType = "FileInfo"; Elements = @('TestFile')
}
)
}
It "<Command>" -TestCases $testCases1 {
param($Command, $CollectionType, $ElementType, $Elements)
$result = $null
. $Command
$result | Should -Not -BeNullOrEmpty
$result.GetType().Name | Should -Be $CollectionType
$genericArgs = $result.GetType().GetGenericArguments()
$genericArgs.Length | Should -Be 1
$genericArgs[0].Name | Should -Be $ElementType
$result.Count | Should -Be $Elements.Length
$result -join ";" | Should -Be ($Elements -join ";")
}
}
}
Describe "Type resolution should prefer assemblies in powershell assembly cache" -Tags "Feature" {
BeforeAll {
$cmdletCode = @'
namespace TestTypeResolution {
using System.Management.Automation;
[Cmdlet("Test", "TypeResolution")]
public class TestTypeResolutionCommand : PSCmdlet {
[Parameter()]
public string Name { get; set; }
protected override void BeginProcessing() {
WriteObject(Name);
}
}
public class TestTypeFoo {
public string Foo { get; set; }
}
}
'@
$dupTypeCode = @'
namespace TestTypeResolution {
public class TestTypeFoo {
public string Bar { get; set; }
}
}
'@
$cmdletDllDir = Join-Path $TestDrive "cmdlet"
$dupTypeDllDir = Join-Path $TestDrive "dupType"
$null = New-Item -Path $cmdletDllDir, $dupTypeDllDir -ItemType Directory -Force
$cmdletDllPath = Join-Path $cmdletDllDir "TestCmdlet.dll"
$dupTypeDllPath = Join-Path $dupTypeDllDir "TestType.dll"
Add-Type $cmdletCode -OutputAssembly $cmdletDllPath
Add-Type $dupTypeCode -OutputAssembly $dupTypeDllPath
$powershell = Join-Path $PSHOME "pwsh"
}
It "validate Type resolution should prefer the assembly loaded by Import-Module" {
$command = @"
Add-Type -Path $dupTypeDllPath
Import-Module $cmdletDllPath
[TestTypeResolution.TestTypeFoo].Assembly.Location
"@
$location = & $powershell -noprofile -command $command
$location | Should -Be $cmdletDllPath
}
}
Describe 'method conversion' -Tags 'CI' {
class M {
[int] Twice([int] $value) { return 2 * $value }
[int] ThriceInstance([int] $value) { return 3 * $value }
static [int] Thrice([int] $value) { return 3 * $value }
static [int] Add([int] $i, [int16] $j) { return $i + $j }
static [int] Apply([int] $value, [Func[int, int]] $function) {
return $function.Invoke($value)
}
static [int] Apply([int] $v1, [Int16] $v2, [Func[int, int16, int]] $function) {
return $function.Invoke($v1, $v2)
}
# check that we can handle at least 72 overloads
static [char] Foo([char] $i) { return $i }
static [char] Foo([char] $i, [char] $j) { return $i }
static [char] Foo([char] $i, [char] $j, [char] $k) { return $i }
static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l) { return $i }
static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l, [char] $m) { return $i }
static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l, [char] $m, [char] $n) { return $i }
static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l, [char] $m, [char] $n, [char] $o) { return $i }
static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l, [char] $m, [char] $n, [char] $o, [char] $p) { return $i }
static [int16] Foo([int16] $i) { return $i }
static [int16] Foo([int16] $i, [int16] $j) { return $i }
static [int16] Foo([int16] $i, [int16] $j, [int16] $k) { return $i }
static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l) { return $i }
static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l, [int16] $m) { return $i }
static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l, [int16] $m, [int16] $n) { return $i }
static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l, [int16] $m, [int16] $n, [int16] $o) { return $i }
static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l, [int16] $m, [int16] $n, [int16] $o, [int16] $p) { return $i }
static [int] Foo([int] $i) { return $i }
static [int] Foo([int] $i, [int] $j) { return $i }
static [int] Foo([int] $i, [int] $j, [int] $k) { return $i }
static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l) { return $i }
static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l, [int] $m) { return $i }
static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l, [int] $m, [int] $n) { return $i }
static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l, [int] $m, [int] $n, [int] $o) { return $i }
static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l, [int] $m, [int] $n, [int] $o, [int] $p) { return $i }
static [UInt32] Foo([UInt32] $i) { return $i }
static [UInt32] Foo([UInt32] $i, [UInt32] $j) { return $i }
static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k) { return $i }
static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l) { return $i }
static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l, [UInt32] $m) { return $i }
static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l, [UInt32] $m, [UInt32] $n) { return $i }
static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l, [UInt32] $m, [UInt32] $n, [UInt32] $o) { return $i }
static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l, [UInt32] $m, [UInt32] $n, [UInt32] $o, [UInt32] $p) { return $i }
static [UInt64] Foo([UInt64] $i) { return $i }
static [UInt64] Foo([UInt64] $i, [UInt64] $j) { return $i }
static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k) { return $i }
static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l) { return $i }
static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l, [UInt64] $m) { return $i }
static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l, [UInt64] $m, [UInt64] $n) { return $i }
static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l, [UInt64] $m, [UInt64] $n, [UInt64] $o) { return $i }
static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l, [UInt64] $m, [UInt64] $n, [UInt64] $o, [UInt64] $p) { return $i }
static [float] Foo([float] $i) { return $i }
static [float] Foo([float] $i, [float] $j) { return $i }
static [float] Foo([float] $i, [float] $j, [float] $k) { return $i }
static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l) { return $i }
static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l, [float] $m) { return $i }
static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l, [float] $m, [float] $n) { return $i }
static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l, [float] $m, [float] $n, [float] $o) { return $i }
static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l, [float] $m, [float] $n, [float] $o, [float] $p) { return $i }
static [double] Foo([double] $i) { return $i }
static [double] Foo([double] $i, [double] $j) { return $i }
static [double] Foo([double] $i, [double] $j, [double] $k) { return $i }
static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l) { return $i }
static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l, [double] $m) { return $i }
static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l, [double] $m, [double] $n) { return $i }
static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l, [double] $m, [double] $n, [double] $o) { return $i }
static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l, [double] $m, [double] $n, [double] $o, [double] $p) { return $i }
static [IntPtr] Foo([IntPtr] $i) { return $i }
static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j) { return $i }
static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k) { return $i }
static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l) { return $i }
static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l, [IntPtr] $m) { return $i }
static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l, [IntPtr] $m, [IntPtr] $n) { return $i }
static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l, [IntPtr] $m, [IntPtr] $n, [IntPtr] $o) { return $i }
static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l, [IntPtr] $m, [IntPtr] $n, [IntPtr] $o, [IntPtr] $p) { return $i }
static [timespan] Foo([timespan] $i) { return $i }
static [timespan] Foo([timespan] $i, [timespan] $j) { return $i }
static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k) { return $i }
static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l) { return $i }
static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l, [timespan] $m) { return $i }
static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l, [timespan] $m, [timespan] $n) { return $i }
static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l, [timespan] $m, [timespan] $n, [timespan] $o) { return $i }
static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l, [timespan] $m, [timespan] $n, [timespan] $o, [timespan] $p) { return $i }
}
It 'converts static method as Func does not throw' {
{ [Func[int, int]] [M]::Thrice } | Should -Not -Throw
}
It 'converts static method as Func is non null' {
([Func[int, int]] [M]::Thrice) | Should -Not -BeNullOrEmpty
}
It 'calls static method as Func' {
$f = [Func[int, int]] [M]::Thrice
[M]::Apply(1, $f) | Should -Be 3
}
It 'calls static method as Func' {
$f = [Func[int, int16, int]] [M]::Add
[M]::Apply(3, 4, $f) | Should -Be 7
}
It 'calls static method as Func no cast' {
[M]::Apply(3, 4, [M]::Add) | Should -Be 7
}
It 'converts instance psmethodinfo to Func' {
$m = [M]::new()
{ [Func[int, int]] $m.Twice } | Should -Not -Throw
$f = [Func[int, int16, int]] [M]::Add
$f.Invoke(2, 6) | Should -Be 8
}
It "can call all overloads of M::Foo" {
[Func[char, char]] $f1 = [M]::Foo
$f1.Invoke(10) | Should -Be 10
[Func[char, char, char]] $f2 = [M]::Foo
$f2.Invoke(10, 1) | Should -Be 10
[Func[char, char, char, char]] $f3 = [M]::Foo
$f3.Invoke(10, 1, 2) | Should -Be 10
[Func[char, char, char, char, char]] $f4 = [M]::Foo
$f4.Invoke(10, 1, 2, 3) | Should -Be 10
[Func[char, char, char, char, char, char]] $f5 = [M]::Foo
$f5.Invoke(10, 1, 2, 3, 4) | Should -Be 10
[Func[char, char, char, char, char, char, char]] $f6 = [M]::Foo
$f6.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10
[Func[char, char, char, char, char, char, char, char]] $f7 = [M]::Foo
$f7.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10
[Func[char, char, char, char, char, char, char, char, char]] $f8 = [M]::Foo
$f8.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10
[Func[int16, int16]] $f9 = [M]::Foo
$f9.Invoke(10) | Should -Be 10
[Func[int16, int16, int16]] $f10 = [M]::Foo
$f10.Invoke(10, 1) | Should -Be 10
[Func[int16, int16, int16, int16]] $f11 = [M]::Foo
$f11.Invoke(10, 1, 2) | Should -Be 10
[Func[int16, int16, int16, int16, int16]] $f12 = [M]::Foo
$f12.Invoke(10, 1, 2, 3) | Should -Be 10
[Func[int16, int16, int16, int16, int16, int16]] $f13 = [M]::Foo
$f13.Invoke(10, 1, 2, 3, 4) | Should -Be 10
[Func[int16, int16, int16, int16, int16, int16, int16]] $f14 = [M]::Foo
$f14.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10
[Func[int16, int16, int16, int16, int16, int16, int16, int16]] $f15 = [M]::Foo
$f15.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10
[Func[int16, int16, int16, int16, int16, int16, int16, int16, int16]] $f16 = [M]::Foo
$f16.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10
[Func[int, int]] $f17 = [M]::Foo
$f17.Invoke(10) | Should -Be 10
[Func[int, int, int]] $f18 = [M]::Foo
$f18.Invoke(10, 1) | Should -Be 10
[Func[int, int, int, int]] $f19 = [M]::Foo
$f19.Invoke(10, 1, 2) | Should -Be 10
[Func[int, int, int, int, int]] $f20 = [M]::Foo
$f20.Invoke(10, 1, 2, 3) | Should -Be 10
[Func[int, int, int, int, int, int]] $f21 = [M]::Foo
$f21.Invoke(10, 1, 2, 3, 4) | Should -Be 10
[Func[int, int, int, int, int, int, int]] $f22 = [M]::Foo
$f22.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10
[Func[int, int, int, int, int, int, int, int]] $f23 = [M]::Foo
$f23.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10
[Func[int, int, int, int, int, int, int, int, int]] $f24 = [M]::Foo
$f24.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10
[Func[UInt32, UInt32]] $f25 = [M]::Foo
$f25.Invoke(10) | Should -Be 10
[Func[UInt32, UInt32, UInt32]] $f26 = [M]::Foo
$f26.Invoke(10, 1) | Should -Be 10
[Func[UInt32, UInt32, UInt32, UInt32]] $f27 = [M]::Foo
$f27.Invoke(10, 1, 2) | Should -Be 10
[Func[UInt32, UInt32, UInt32, UInt32, UInt32]] $f28 = [M]::Foo
$f28.Invoke(10, 1, 2, 3) | Should -Be 10
[Func[UInt32, UInt32, UInt32, UInt32, UInt32, UInt32]] $f29 = [M]::Foo
$f29.Invoke(10, 1, 2, 3, 4) | Should -Be 10
[Func[UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32]] $f30 = [M]::Foo
$f30.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10
[Func[UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32]] $f31 = [M]::Foo
$f31.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10
[Func[UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32]] $f32 = [M]::Foo
$f32.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10
[Func[UInt64, UInt64]] $f33 = [M]::Foo
$f33.Invoke(10) | Should -Be 10
[Func[UInt64, UInt64, UInt64]] $f34 = [M]::Foo
$f34.Invoke(10, 1) | Should -Be 10
[Func[UInt64, UInt64, UInt64, UInt64]] $f35 = [M]::Foo
$f35.Invoke(10, 1, 2) | Should -Be 10
[Func[UInt64, UInt64, UInt64, UInt64, UInt64]] $f36 = [M]::Foo
$f36.Invoke(10, 1, 2, 3) | Should -Be 10
[Func[UInt64, UInt64, UInt64, UInt64, UInt64, UInt64]] $f37 = [M]::Foo
$f37.Invoke(10, 1, 2, 3, 4) | Should -Be 10
[Func[UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64]] $f38 = [M]::Foo
$f38.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10
[Func[UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64]] $f39 = [M]::Foo
$f39.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10
[Func[UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64]] $f40 = [M]::Foo
$f40.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10
[Func[float, float]] $f41 = [M]::Foo
$f41.Invoke(10) | Should -Be 10
[Func[float, float, float]] $f42 = [M]::Foo
$f42.Invoke(10, 1) | Should -Be 10
[Func[float, float, float, float]] $f43 = [M]::Foo
$f43.Invoke(10, 1, 2) | Should -Be 10
[Func[float, float, float, float, float]] $f44 = [M]::Foo
$f44.Invoke(10, 1, 2, 3) | Should -Be 10
[Func[float, float, float, float, float, float]] $f45 = [M]::Foo
$f45.Invoke(10, 1, 2, 3, 4) | Should -Be 10
[Func[float, float, float, float, float, float, float]] $f46 = [M]::Foo
$f46.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10
[Func[float, float, float, float, float, float, float, float]] $f47 = [M]::Foo
$f47.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10
[Func[float, float, float, float, float, float, float, float, float]] $f48 = [M]::Foo
$f48.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10
[Func[double, double]] $f49 = [M]::Foo
$f49.Invoke(10) | Should -Be 10
[Func[double, double, double]] $f50 = [M]::Foo
$f50.Invoke(10, 1) | Should -Be 10
[Func[double, double, double, double]] $f51 = [M]::Foo
$f51.Invoke(10, 1, 2) | Should -Be 10
[Func[double, double, double, double, double]] $f52 = [M]::Foo
$f52.Invoke(10, 1, 2, 3) | Should -Be 10
[Func[double, double, double, double, double, double]] $f53 = [M]::Foo
$f53.Invoke(10, 1, 2, 3, 4) | Should -Be 10
[Func[double, double, double, double, double, double, double]] $f54 = [M]::Foo
$f54.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10
[Func[double, double, double, double, double, double, double, double]] $f55 = [M]::Foo
$f55.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10
[Func[double, double, double, double, double, double, double, double, double]] $f56 = [M]::Foo
$f56.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10
[Func[IntPtr, IntPtr]] $f57 = [M]::Foo
$f57.Invoke(10) | Should -Be 10
[Func[IntPtr, IntPtr, IntPtr]] $f58 = [M]::Foo
$f58.Invoke(10, 1) | Should -Be 10
[Func[IntPtr, IntPtr, IntPtr, IntPtr]] $f59 = [M]::Foo
$f59.Invoke(10, 1, 2) | Should -Be 10
[Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f60 = [M]::Foo
$f60.Invoke(10, 1, 2, 3) | Should -Be 10
[Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f61 = [M]::Foo
$f61.Invoke(10, 1, 2, 3, 4) | Should -Be 10
[Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f62 = [M]::Foo
$f62.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10
[Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f63 = [M]::Foo
$f63.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10
[Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f64 = [M]::Foo
$f64.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10
$timespan = [timespan]::FromMinutes(62)
[Func[timespan, timespan]] $f65 = [M]::Foo
$f65.Invoke($timeSpan) | Should -Be $timeSpan
[Func[timespan, timespan, timespan]] $f66 = [M]::Foo
$f66.Invoke($timeSpan, [Timespan]::Zero) | Should -Be $timeSpan
[Func[timespan, timespan, timespan, timespan]] $f67 = [M]::Foo
$f67.Invoke($timeSpan, [Timespan]::Zero, [Timespan]::Zero) | Should -Be $timeSpan
[Func[timespan, timespan, timespan, timespan, timespan]] $f68 = [M]::Foo
$f68.Invoke($timeSpan, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero) | Should -Be $timeSpan
[Func[timespan, timespan, timespan, timespan, timespan, timespan]] $f69 = [M]::Foo
$f69.Invoke($timeSpan, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero) | Should -Be $timeSpan
[Func[timespan, timespan, timespan, timespan, timespan, timespan, timespan]] $f70 = [M]::Foo
$f70.Invoke($timeSpan, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero) | Should -Be $timeSpan
[Func[timespan, timespan, timespan, timespan, timespan, timespan, timespan, timespan]] $f71 = [M]::Foo
$f71.Invoke($timeSpan, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero) | Should -Be $timeSpan
[Func[timespan, timespan, timespan, timespan, timespan, timespan, timespan, timespan, timespan]] $f72 = [M]::Foo
$f72.Invoke($timeSpan, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero) | Should -Be $timeSpan
}
Further improve PSMethod to Delegate conversion (#6851) Refactor code to make it easier to maintain and a little faster. Changes are as follows: 1. Support finding a matching signature with variance. But make PowerShell prefer exact match over a match with variance. 2. The metadata signatures in `PSMethod<..>` are generated based on the array of method overloads in `MethodCacheEntry.MethodInformationStructures`, in the exact same order. So in `LanguagePrimitive.ConvertViaParseMethod`, when we try to figure out if there is a match using the metadata signatures in `PSMethod<..>`, we can get the index of the matching signature, and the same index should locate the matching method in `MethodCacheEntry.MethodInformationStructures`. Therefore, we don't need to compare signatures again in the actual conversion method, and instead, we can just leverage the index we found when figuring out the conversion in `ConvertViaParseMethod`. - This gets rid of the reflection call `GetMethod("Invoke")` and the subsequent signature comparisons in the final conversion method. - Also, when comparing signatures using `PSMethod<..>` in `ConvertViaParseMethod`, we can just use the generic argument types of each `Func<..>` metadata type, instead of calling `GetMethod("Invoke")` and then `GetParameters()`. This makes the code for comparing signatures simpler (the type `SignatureComparator`). - Move `MatchesPSMethodProjectedType` from `PSMemberInfo.cs` to the type `SignatureComparator` in `LanguagePrimitives.cs`, as it's closely related to the signature comparison. Also, renamed it to `ProjectedTypeMatchesTargetType`. - These changes make PSMethod-to-Delegate conversion a little faster, but no big improvement, as the true bottleneck probably is in delegate creation(?). Actually, the performance of this conversion is not critical at all at this moment because this feature should rarely be used in any hot script path. So this exercise is mainly for fun. 3. Remove `PSEnum<T>`. We can directly use enum types when constructing the metadata type `Func<..>`. 4. Remove the code that generates metadata signatures for generic method definitions (call `MakeGenericMethod` with fake types like `GenericType0`, `GenericType1`). This is because: - We don't support convert generic method to delegate today, so may be better not spending time on preparing the metadata signature types for those methods. - When the day comes that we need to support it, it's better to use generic argument types directly to construct the `Func<..>` metadata types. I left comments in `GetMethodGroupType` method in `PSMemberInfo.cs` to explain why that approach is better.
2018-05-15 20:45:04 +02:00
enum E {
Day;
Week;
Year;
}
class N {
## Attempt to convert methods to Func<System.IO.FileInfo, string, object>.
## Different methods with same overload signatures.
## The second and third overloads match the target delegate with variance.
[string] GetA([int] $i, [string] $s) { return "GetA-int-string-string" }
Further improve PSMethod to Delegate conversion (#6851) Refactor code to make it easier to maintain and a little faster. Changes are as follows: 1. Support finding a matching signature with variance. But make PowerShell prefer exact match over a match with variance. 2. The metadata signatures in `PSMethod<..>` are generated based on the array of method overloads in `MethodCacheEntry.MethodInformationStructures`, in the exact same order. So in `LanguagePrimitive.ConvertViaParseMethod`, when we try to figure out if there is a match using the metadata signatures in `PSMethod<..>`, we can get the index of the matching signature, and the same index should locate the matching method in `MethodCacheEntry.MethodInformationStructures`. Therefore, we don't need to compare signatures again in the actual conversion method, and instead, we can just leverage the index we found when figuring out the conversion in `ConvertViaParseMethod`. - This gets rid of the reflection call `GetMethod("Invoke")` and the subsequent signature comparisons in the final conversion method. - Also, when comparing signatures using `PSMethod<..>` in `ConvertViaParseMethod`, we can just use the generic argument types of each `Func<..>` metadata type, instead of calling `GetMethod("Invoke")` and then `GetParameters()`. This makes the code for comparing signatures simpler (the type `SignatureComparator`). - Move `MatchesPSMethodProjectedType` from `PSMemberInfo.cs` to the type `SignatureComparator` in `LanguagePrimitives.cs`, as it's closely related to the signature comparison. Also, renamed it to `ProjectedTypeMatchesTargetType`. - These changes make PSMethod-to-Delegate conversion a little faster, but no big improvement, as the true bottleneck probably is in delegate creation(?). Actually, the performance of this conversion is not critical at all at this moment because this feature should rarely be used in any hot script path. So this exercise is mainly for fun. 3. Remove `PSEnum<T>`. We can directly use enum types when constructing the metadata type `Func<..>`. 4. Remove the code that generates metadata signatures for generic method definitions (call `MakeGenericMethod` with fake types like `GenericType0`, `GenericType1`). This is because: - We don't support convert generic method to delegate today, so may be better not spending time on preparing the metadata signature types for those methods. - When the day comes that we need to support it, it's better to use generic argument types directly to construct the `Func<..>` metadata types. I left comments in `GetMethodGroupType` method in `PSMemberInfo.cs` to explain why that approach is better.
2018-05-15 20:45:04 +02:00
[string] GetA([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetA-filesysteminfo-object-string" }
[string] GetA([System.IO.FileInfo] $finfo, [object] $o) { return "GetA-fileinfo-object-string" }
Further improve PSMethod to Delegate conversion (#6851) Refactor code to make it easier to maintain and a little faster. Changes are as follows: 1. Support finding a matching signature with variance. But make PowerShell prefer exact match over a match with variance. 2. The metadata signatures in `PSMethod<..>` are generated based on the array of method overloads in `MethodCacheEntry.MethodInformationStructures`, in the exact same order. So in `LanguagePrimitive.ConvertViaParseMethod`, when we try to figure out if there is a match using the metadata signatures in `PSMethod<..>`, we can get the index of the matching signature, and the same index should locate the matching method in `MethodCacheEntry.MethodInformationStructures`. Therefore, we don't need to compare signatures again in the actual conversion method, and instead, we can just leverage the index we found when figuring out the conversion in `ConvertViaParseMethod`. - This gets rid of the reflection call `GetMethod("Invoke")` and the subsequent signature comparisons in the final conversion method. - Also, when comparing signatures using `PSMethod<..>` in `ConvertViaParseMethod`, we can just use the generic argument types of each `Func<..>` metadata type, instead of calling `GetMethod("Invoke")` and then `GetParameters()`. This makes the code for comparing signatures simpler (the type `SignatureComparator`). - Move `MatchesPSMethodProjectedType` from `PSMemberInfo.cs` to the type `SignatureComparator` in `LanguagePrimitives.cs`, as it's closely related to the signature comparison. Also, renamed it to `ProjectedTypeMatchesTargetType`. - These changes make PSMethod-to-Delegate conversion a little faster, but no big improvement, as the true bottleneck probably is in delegate creation(?). Actually, the performance of this conversion is not critical at all at this moment because this feature should rarely be used in any hot script path. So this exercise is mainly for fun. 3. Remove `PSEnum<T>`. We can directly use enum types when constructing the metadata type `Func<..>`. 4. Remove the code that generates metadata signatures for generic method definitions (call `MakeGenericMethod` with fake types like `GenericType0`, `GenericType1`). This is because: - We don't support convert generic method to delegate today, so may be better not spending time on preparing the metadata signature types for those methods. - When the day comes that we need to support it, it's better to use generic argument types directly to construct the `Func<..>` metadata types. I left comments in `GetMethodGroupType` method in `PSMemberInfo.cs` to explain why that approach is better.
2018-05-15 20:45:04 +02:00
[string] GetAPrime([int] $i, [string] $s) { return "GetAPrime-int-string-string" }
Further improve PSMethod to Delegate conversion (#6851) Refactor code to make it easier to maintain and a little faster. Changes are as follows: 1. Support finding a matching signature with variance. But make PowerShell prefer exact match over a match with variance. 2. The metadata signatures in `PSMethod<..>` are generated based on the array of method overloads in `MethodCacheEntry.MethodInformationStructures`, in the exact same order. So in `LanguagePrimitive.ConvertViaParseMethod`, when we try to figure out if there is a match using the metadata signatures in `PSMethod<..>`, we can get the index of the matching signature, and the same index should locate the matching method in `MethodCacheEntry.MethodInformationStructures`. Therefore, we don't need to compare signatures again in the actual conversion method, and instead, we can just leverage the index we found when figuring out the conversion in `ConvertViaParseMethod`. - This gets rid of the reflection call `GetMethod("Invoke")` and the subsequent signature comparisons in the final conversion method. - Also, when comparing signatures using `PSMethod<..>` in `ConvertViaParseMethod`, we can just use the generic argument types of each `Func<..>` metadata type, instead of calling `GetMethod("Invoke")` and then `GetParameters()`. This makes the code for comparing signatures simpler (the type `SignatureComparator`). - Move `MatchesPSMethodProjectedType` from `PSMemberInfo.cs` to the type `SignatureComparator` in `LanguagePrimitives.cs`, as it's closely related to the signature comparison. Also, renamed it to `ProjectedTypeMatchesTargetType`. - These changes make PSMethod-to-Delegate conversion a little faster, but no big improvement, as the true bottleneck probably is in delegate creation(?). Actually, the performance of this conversion is not critical at all at this moment because this feature should rarely be used in any hot script path. So this exercise is mainly for fun. 3. Remove `PSEnum<T>`. We can directly use enum types when constructing the metadata type `Func<..>`. 4. Remove the code that generates metadata signatures for generic method definitions (call `MakeGenericMethod` with fake types like `GenericType0`, `GenericType1`). This is because: - We don't support convert generic method to delegate today, so may be better not spending time on preparing the metadata signature types for those methods. - When the day comes that we need to support it, it's better to use generic argument types directly to construct the `Func<..>` metadata types. I left comments in `GetMethodGroupType` method in `PSMemberInfo.cs` to explain why that approach is better.
2018-05-15 20:45:04 +02:00
[string] GetAPrime([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetAPrime-filesysteminfo-object-string" }
[string] GetAPrime([System.IO.FileInfo] $finfo, [object] $o) { return "GetAPrime-fileinfo-object-string" }
Further improve PSMethod to Delegate conversion (#6851) Refactor code to make it easier to maintain and a little faster. Changes are as follows: 1. Support finding a matching signature with variance. But make PowerShell prefer exact match over a match with variance. 2. The metadata signatures in `PSMethod<..>` are generated based on the array of method overloads in `MethodCacheEntry.MethodInformationStructures`, in the exact same order. So in `LanguagePrimitive.ConvertViaParseMethod`, when we try to figure out if there is a match using the metadata signatures in `PSMethod<..>`, we can get the index of the matching signature, and the same index should locate the matching method in `MethodCacheEntry.MethodInformationStructures`. Therefore, we don't need to compare signatures again in the actual conversion method, and instead, we can just leverage the index we found when figuring out the conversion in `ConvertViaParseMethod`. - This gets rid of the reflection call `GetMethod("Invoke")` and the subsequent signature comparisons in the final conversion method. - Also, when comparing signatures using `PSMethod<..>` in `ConvertViaParseMethod`, we can just use the generic argument types of each `Func<..>` metadata type, instead of calling `GetMethod("Invoke")` and then `GetParameters()`. This makes the code for comparing signatures simpler (the type `SignatureComparator`). - Move `MatchesPSMethodProjectedType` from `PSMemberInfo.cs` to the type `SignatureComparator` in `LanguagePrimitives.cs`, as it's closely related to the signature comparison. Also, renamed it to `ProjectedTypeMatchesTargetType`. - These changes make PSMethod-to-Delegate conversion a little faster, but no big improvement, as the true bottleneck probably is in delegate creation(?). Actually, the performance of this conversion is not critical at all at this moment because this feature should rarely be used in any hot script path. So this exercise is mainly for fun. 3. Remove `PSEnum<T>`. We can directly use enum types when constructing the metadata type `Func<..>`. 4. Remove the code that generates metadata signatures for generic method definitions (call `MakeGenericMethod` with fake types like `GenericType0`, `GenericType1`). This is because: - We don't support convert generic method to delegate today, so may be better not spending time on preparing the metadata signature types for those methods. - When the day comes that we need to support it, it's better to use generic argument types directly to construct the `Func<..>` metadata types. I left comments in `GetMethodGroupType` method in `PSMemberInfo.cs` to explain why that approach is better.
2018-05-15 20:45:04 +02:00
static [string] GetAStatic([int] $i, [string] $s) { return "GetAStatic-int-string-string" }
Further improve PSMethod to Delegate conversion (#6851) Refactor code to make it easier to maintain and a little faster. Changes are as follows: 1. Support finding a matching signature with variance. But make PowerShell prefer exact match over a match with variance. 2. The metadata signatures in `PSMethod<..>` are generated based on the array of method overloads in `MethodCacheEntry.MethodInformationStructures`, in the exact same order. So in `LanguagePrimitive.ConvertViaParseMethod`, when we try to figure out if there is a match using the metadata signatures in `PSMethod<..>`, we can get the index of the matching signature, and the same index should locate the matching method in `MethodCacheEntry.MethodInformationStructures`. Therefore, we don't need to compare signatures again in the actual conversion method, and instead, we can just leverage the index we found when figuring out the conversion in `ConvertViaParseMethod`. - This gets rid of the reflection call `GetMethod("Invoke")` and the subsequent signature comparisons in the final conversion method. - Also, when comparing signatures using `PSMethod<..>` in `ConvertViaParseMethod`, we can just use the generic argument types of each `Func<..>` metadata type, instead of calling `GetMethod("Invoke")` and then `GetParameters()`. This makes the code for comparing signatures simpler (the type `SignatureComparator`). - Move `MatchesPSMethodProjectedType` from `PSMemberInfo.cs` to the type `SignatureComparator` in `LanguagePrimitives.cs`, as it's closely related to the signature comparison. Also, renamed it to `ProjectedTypeMatchesTargetType`. - These changes make PSMethod-to-Delegate conversion a little faster, but no big improvement, as the true bottleneck probably is in delegate creation(?). Actually, the performance of this conversion is not critical at all at this moment because this feature should rarely be used in any hot script path. So this exercise is mainly for fun. 3. Remove `PSEnum<T>`. We can directly use enum types when constructing the metadata type `Func<..>`. 4. Remove the code that generates metadata signatures for generic method definitions (call `MakeGenericMethod` with fake types like `GenericType0`, `GenericType1`). This is because: - We don't support convert generic method to delegate today, so may be better not spending time on preparing the metadata signature types for those methods. - When the day comes that we need to support it, it's better to use generic argument types directly to construct the `Func<..>` metadata types. I left comments in `GetMethodGroupType` method in `PSMemberInfo.cs` to explain why that approach is better.
2018-05-15 20:45:04 +02:00
static [string] GetAStatic([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetAStatic-filesysteminfo-object-string" }
static [string] GetAStatic([System.IO.FileInfo] $finfo, [object] $o) { return "GetAStatic-fileinfo-object-string" }
Further improve PSMethod to Delegate conversion (#6851) Refactor code to make it easier to maintain and a little faster. Changes are as follows: 1. Support finding a matching signature with variance. But make PowerShell prefer exact match over a match with variance. 2. The metadata signatures in `PSMethod<..>` are generated based on the array of method overloads in `MethodCacheEntry.MethodInformationStructures`, in the exact same order. So in `LanguagePrimitive.ConvertViaParseMethod`, when we try to figure out if there is a match using the metadata signatures in `PSMethod<..>`, we can get the index of the matching signature, and the same index should locate the matching method in `MethodCacheEntry.MethodInformationStructures`. Therefore, we don't need to compare signatures again in the actual conversion method, and instead, we can just leverage the index we found when figuring out the conversion in `ConvertViaParseMethod`. - This gets rid of the reflection call `GetMethod("Invoke")` and the subsequent signature comparisons in the final conversion method. - Also, when comparing signatures using `PSMethod<..>` in `ConvertViaParseMethod`, we can just use the generic argument types of each `Func<..>` metadata type, instead of calling `GetMethod("Invoke")` and then `GetParameters()`. This makes the code for comparing signatures simpler (the type `SignatureComparator`). - Move `MatchesPSMethodProjectedType` from `PSMemberInfo.cs` to the type `SignatureComparator` in `LanguagePrimitives.cs`, as it's closely related to the signature comparison. Also, renamed it to `ProjectedTypeMatchesTargetType`. - These changes make PSMethod-to-Delegate conversion a little faster, but no big improvement, as the true bottleneck probably is in delegate creation(?). Actually, the performance of this conversion is not critical at all at this moment because this feature should rarely be used in any hot script path. So this exercise is mainly for fun. 3. Remove `PSEnum<T>`. We can directly use enum types when constructing the metadata type `Func<..>`. 4. Remove the code that generates metadata signatures for generic method definitions (call `MakeGenericMethod` with fake types like `GenericType0`, `GenericType1`). This is because: - We don't support convert generic method to delegate today, so may be better not spending time on preparing the metadata signature types for those methods. - When the day comes that we need to support it, it's better to use generic argument types directly to construct the `Func<..>` metadata types. I left comments in `GetMethodGroupType` method in `PSMemberInfo.cs` to explain why that approach is better.
2018-05-15 20:45:04 +02:00
## Different methods with same overload signatures.
## The first overload matches the target delegate with variance,
## while the second overload matches the target delegate exactly.
[string] GetB([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetB-filesysteminfo-object-string" }
[object] GetB([System.IO.FileInfo] $finfo, [string] $s) { return "GetB-fileinfo-string-object" }
[string] GetB([datetime] $d) { return "GetB-datetime-string" }
Further improve PSMethod to Delegate conversion (#6851) Refactor code to make it easier to maintain and a little faster. Changes are as follows: 1. Support finding a matching signature with variance. But make PowerShell prefer exact match over a match with variance. 2. The metadata signatures in `PSMethod<..>` are generated based on the array of method overloads in `MethodCacheEntry.MethodInformationStructures`, in the exact same order. So in `LanguagePrimitive.ConvertViaParseMethod`, when we try to figure out if there is a match using the metadata signatures in `PSMethod<..>`, we can get the index of the matching signature, and the same index should locate the matching method in `MethodCacheEntry.MethodInformationStructures`. Therefore, we don't need to compare signatures again in the actual conversion method, and instead, we can just leverage the index we found when figuring out the conversion in `ConvertViaParseMethod`. - This gets rid of the reflection call `GetMethod("Invoke")` and the subsequent signature comparisons in the final conversion method. - Also, when comparing signatures using `PSMethod<..>` in `ConvertViaParseMethod`, we can just use the generic argument types of each `Func<..>` metadata type, instead of calling `GetMethod("Invoke")` and then `GetParameters()`. This makes the code for comparing signatures simpler (the type `SignatureComparator`). - Move `MatchesPSMethodProjectedType` from `PSMemberInfo.cs` to the type `SignatureComparator` in `LanguagePrimitives.cs`, as it's closely related to the signature comparison. Also, renamed it to `ProjectedTypeMatchesTargetType`. - These changes make PSMethod-to-Delegate conversion a little faster, but no big improvement, as the true bottleneck probably is in delegate creation(?). Actually, the performance of this conversion is not critical at all at this moment because this feature should rarely be used in any hot script path. So this exercise is mainly for fun. 3. Remove `PSEnum<T>`. We can directly use enum types when constructing the metadata type `Func<..>`. 4. Remove the code that generates metadata signatures for generic method definitions (call `MakeGenericMethod` with fake types like `GenericType0`, `GenericType1`). This is because: - We don't support convert generic method to delegate today, so may be better not spending time on preparing the metadata signature types for those methods. - When the day comes that we need to support it, it's better to use generic argument types directly to construct the `Func<..>` metadata types. I left comments in `GetMethodGroupType` method in `PSMemberInfo.cs` to explain why that approach is better.
2018-05-15 20:45:04 +02:00
[string] GetBPrime([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetBPrime-filesysteminfo-object-string" }
[object] GetBPrime([System.IO.FileInfo] $finfo, [string] $s) { return "GetBPrime-fileinfo-string-object" }
[string] GetBPrime([datetime] $d) { return "GetBPrime-datetime-string" }
Further improve PSMethod to Delegate conversion (#6851) Refactor code to make it easier to maintain and a little faster. Changes are as follows: 1. Support finding a matching signature with variance. But make PowerShell prefer exact match over a match with variance. 2. The metadata signatures in `PSMethod<..>` are generated based on the array of method overloads in `MethodCacheEntry.MethodInformationStructures`, in the exact same order. So in `LanguagePrimitive.ConvertViaParseMethod`, when we try to figure out if there is a match using the metadata signatures in `PSMethod<..>`, we can get the index of the matching signature, and the same index should locate the matching method in `MethodCacheEntry.MethodInformationStructures`. Therefore, we don't need to compare signatures again in the actual conversion method, and instead, we can just leverage the index we found when figuring out the conversion in `ConvertViaParseMethod`. - This gets rid of the reflection call `GetMethod("Invoke")` and the subsequent signature comparisons in the final conversion method. - Also, when comparing signatures using `PSMethod<..>` in `ConvertViaParseMethod`, we can just use the generic argument types of each `Func<..>` metadata type, instead of calling `GetMethod("Invoke")` and then `GetParameters()`. This makes the code for comparing signatures simpler (the type `SignatureComparator`). - Move `MatchesPSMethodProjectedType` from `PSMemberInfo.cs` to the type `SignatureComparator` in `LanguagePrimitives.cs`, as it's closely related to the signature comparison. Also, renamed it to `ProjectedTypeMatchesTargetType`. - These changes make PSMethod-to-Delegate conversion a little faster, but no big improvement, as the true bottleneck probably is in delegate creation(?). Actually, the performance of this conversion is not critical at all at this moment because this feature should rarely be used in any hot script path. So this exercise is mainly for fun. 3. Remove `PSEnum<T>`. We can directly use enum types when constructing the metadata type `Func<..>`. 4. Remove the code that generates metadata signatures for generic method definitions (call `MakeGenericMethod` with fake types like `GenericType0`, `GenericType1`). This is because: - We don't support convert generic method to delegate today, so may be better not spending time on preparing the metadata signature types for those methods. - When the day comes that we need to support it, it's better to use generic argument types directly to construct the `Func<..>` metadata types. I left comments in `GetMethodGroupType` method in `PSMemberInfo.cs` to explain why that approach is better.
2018-05-15 20:45:04 +02:00
static [string] GetBStatic([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetBStatic-filesysteminfo-object-string" }
static [object] GetBStatic([System.IO.FileInfo] $finfo, [string] $s) { return "GetBStatic-fileinfo-string-object" }
static [string] GetBStatic([datetime] $d) { return "GetBStatic-datetime-string" }
Further improve PSMethod to Delegate conversion (#6851) Refactor code to make it easier to maintain and a little faster. Changes are as follows: 1. Support finding a matching signature with variance. But make PowerShell prefer exact match over a match with variance. 2. The metadata signatures in `PSMethod<..>` are generated based on the array of method overloads in `MethodCacheEntry.MethodInformationStructures`, in the exact same order. So in `LanguagePrimitive.ConvertViaParseMethod`, when we try to figure out if there is a match using the metadata signatures in `PSMethod<..>`, we can get the index of the matching signature, and the same index should locate the matching method in `MethodCacheEntry.MethodInformationStructures`. Therefore, we don't need to compare signatures again in the actual conversion method, and instead, we can just leverage the index we found when figuring out the conversion in `ConvertViaParseMethod`. - This gets rid of the reflection call `GetMethod("Invoke")` and the subsequent signature comparisons in the final conversion method. - Also, when comparing signatures using `PSMethod<..>` in `ConvertViaParseMethod`, we can just use the generic argument types of each `Func<..>` metadata type, instead of calling `GetMethod("Invoke")` and then `GetParameters()`. This makes the code for comparing signatures simpler (the type `SignatureComparator`). - Move `MatchesPSMethodProjectedType` from `PSMemberInfo.cs` to the type `SignatureComparator` in `LanguagePrimitives.cs`, as it's closely related to the signature comparison. Also, renamed it to `ProjectedTypeMatchesTargetType`. - These changes make PSMethod-to-Delegate conversion a little faster, but no big improvement, as the true bottleneck probably is in delegate creation(?). Actually, the performance of this conversion is not critical at all at this moment because this feature should rarely be used in any hot script path. So this exercise is mainly for fun. 3. Remove `PSEnum<T>`. We can directly use enum types when constructing the metadata type `Func<..>`. 4. Remove the code that generates metadata signatures for generic method definitions (call `MakeGenericMethod` with fake types like `GenericType0`, `GenericType1`). This is because: - We don't support convert generic method to delegate today, so may be better not spending time on preparing the metadata signature types for those methods. - When the day comes that we need to support it, it's better to use generic argument types directly to construct the `Func<..>` metadata types. I left comments in `GetMethodGroupType` method in `PSMemberInfo.cs` to explain why that approach is better.
2018-05-15 20:45:04 +02:00
## Test enum parameter type
[object] GetC([E] $e) { return $e.ToString() }
}
It "Different method overloads with same signatures/orders should have same PSMethod type" {
$n = [N]::new()
$n.GetA.GetType() | Should -Be ($n.GetAPrime.GetType())
$n.GetA.GetType() | Should -Be ([N]::GetAStatic.GetType())
$n.GetB.GetType() | Should -Be ($n.GetBPrime.GetType())
$n.GetB.GetType() | Should -Be ([N]::GetBStatic.GetType())
}
It "Match signature with variance and use the first match when there is no exact match" {
$n = [N]::new()
[Func[[System.IO.FileInfo], [string], [object]]] $f = $n.GetA
$f.Invoke($null, $null) | Should -BeExactly "GetA-filesysteminfo-object-string"
$f = $n.GetAPrime ## $n.GetAPrime has the same type as $n.GetA, so it should hit the conversion cache
$f.Invoke([System.IO.FileInfo]::new("aaa"), "bbb") | Should -BeExactly "GetAPrime-filesysteminfo-object-string"
$f = [N]::GetAStatic ## [N]::GetAStatic has the same type as $n.GetA, so it should hit the conversion cache
$f.Invoke($null, "") | Should -BeExactly "GetAStatic-filesysteminfo-object-string"
}
It "Exact match is preferred over match with variance" {
$n = [N]::new()
[Func[[System.IO.FileInfo], [string], [object]]] $f = $n.GetB
$f.Invoke($null, $null) | Should -BeExactly "GetB-fileinfo-string-object"
$f = $n.GetBPrime ## $n.GetBPrime has the same type as $n.GetB, so it should hit the conversion cache
$f.Invoke([System.IO.FileInfo]::new("ccc"), "ddd") | Should -BeExactly "GetBPrime-fileinfo-string-object"
$f = [N]::GetBStatic ## [N]::GetBStatic has the same type as $n.GetB, so it should hit the conversion cache
$f.Invoke($null, "") | Should -BeExactly "GetBStatic-fileinfo-string-object"
}
It "Test enum type parameter" {
$n = [N]::new()
[Func[[E], [object]]] $f = $n.GetC
$f.Invoke([E]::Week) | Should -BeExactly "Week"
}
It "Test fail-to-convert code path" {
$n = [N]::new()
{ [System.Management.Automation.LanguagePrimitives]::ConvertTo($n.GetC, [Func[[int], [object]]]) } | Should -Throw -ErrorId "PSInvalidCastException"
}
$TestCases = @(
@{ Number = "100y"; Value = "100"; Type = [int] }
@{ Number = "100uy"; Value = "100"; Type = [double] }
@{ Number = "1200u"; Value = "1200"; Type = [short] }
@{ Number = "1200L"; Value = "1200"; Type = [int] }
@{ Number = "127ul"; Value = "127"; Type = [ulong] }
@{ Number = "127d"; Value = "127"; Type = [byte] }
@{ Number = "127s"; Value = "127"; Type = [sbyte] }
@{ Number = "127y"; Value = "127"; Type = [uint] }
@{ Number = "100n"; Value = "100"; Type = [int] }
@{ Number = "1234s"; Value = "1234"; Type = [bigint] }
)
It "Correctly casts <Number> to value <Value> as type <Type>" -TestCases $TestCases {
param($Number, $Value, $Type)
$Result = $Number -as $Type
$Result | Should -Be $Value
$Result | Should -BeOfType $Type
}
$TestCases = @(
@{ Number = "200y" }
@{ Number = "300uy" }
@{ Number = "70000us" }
@{ Number = "40000s" }
)
It "Fails to cast invalid PowerShell-Style suffixed numeral <Number>" -TestCases $TestCases {
param($Number)
$Result = $Number -as [int]
$Result | Should -BeNullOrEmpty
}
}
Describe 'float/double precision when converting to string' -Tags "CI" {
It "<SourceType>-to-[string] conversion in PowerShell should use the precision specifier <Format>" -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() }
@{ SourceType = [double]; Format = "G15"; ValueScript = { [System.Math]::PI }; StringConversionResult = [System.Math]::PI.ToString("G15"); ToStringResult = [System.Math]::PI.ToString() }
@{ SourceType = [float]; Format = "G7"; ValueScript = { [float]$f = 1.1; ($f * 3).ToSingle([cultureinfo]::InvariantCulture) }; StringConversionResult = "3.3"; ToStringResult = "3.3000002" }
@{ SourceType = [float]; Format = "G7"; ValueScript = { [float]$f = 1.1; ($f * 6).ToSingle([cultureinfo]::InvariantCulture) }; StringConversionResult = "6.6"; ToStringResult = "6.6000004" }
@{ SourceType = [float]; Format = "G7"; ValueScript = { [float]::MaxValue }; StringConversionResult = [float]::MaxValue.ToString("G7"); ToStringResult = [float]::MaxValue.ToString() }
@{ SourceType = [float]; Format = "G7"; ValueScript = { [float]::MinValue }; StringConversionResult = [float]::MinValue.ToString("G7"); ToStringResult = [float]::MinValue.ToString() }
) {
param($SourceType, $ValueScript, $StringConversionResult, $ToStringResult)
$value = & $ValueScript
$value | Should -BeOfType $SourceType
$value.ToString() | Should -BeExactly $ToStringResult
$value -as [string] | Should -BeExactly $StringConversionResult
[string]$value | Should -BeExactly $StringConversionResult
[System.Management.Automation.LanguagePrimitives]::ConvertTo($value, [string]) | Should -BeExactly $StringConversionResult
"$value" | Should -BeExactly $StringConversionResult
$value | Out-String | ForEach-Object -MemberName Trim | Should -BeExactly $StringConversionResult
}
}
Describe 'Casting Behaviour of Boolean/Null to Numeral' -Tags CI {
BeforeAll {
$NullToNumeral = @(
@{ Type = [sbyte]; ExpectedResult = 0y }
@{ Type = [byte]; ExpectedResult = 0uy }
@{ Type = [short]; ExpectedResult = 0s }
@{ Type = [ushort]; ExpectedResult = 0us }
@{ Type = [int]; ExpectedResult = 0 }
@{ Type = [uint]; ExpectedResult = 0u }
@{ Type = [long]; ExpectedResult = 0l }
@{ Type = [ulong]; ExpectedResult = 0ul }
@{ Type = [decimal]; ExpectedResult = 0d }
@{ Type = [float]; ExpectedResult = [float]0 }
@{ Type = [double]; ExpectedResult = 0.0 }
@{ Type = [bigint]; ExpectedResult = 0n }
)
$BoolToNumeral = @(
@{ Type = [sbyte]; Value = $true; ExpectedResult = 1y }
@{ Type = [sbyte]; Value = $false; ExpectedResult = 0y }
@{ Type = [byte]; Value = $true; ExpectedResult = 1uy }
@{ Type = [byte]; Value = $false; ExpectedResult = 0uy }
@{ Type = [short]; Value = $true; ExpectedResult = 1s }
@{ Type = [short]; Value = $false; ExpectedResult = 0s }
@{ Type = [ushort]; Value = $true; ExpectedResult = 1us }
@{ Type = [ushort]; Value = $false; ExpectedResult = 0us }
@{ Type = [int]; Value = $true; ExpectedResult = 1 }
@{ Type = [int]; Value = $false; ExpectedResult = 0 }
@{ Type = [uint]; Value = $true; ExpectedResult = 1u }
@{ Type = [uint]; Value = $false; ExpectedResult = 0u }
@{ Type = [long]; Value = $true; ExpectedResult = 1l }
@{ Type = [long]; Value = $false; ExpectedResult = 0l }
@{ Type = [ulong]; Value = $true; ExpectedResult = 1ul }
@{ Type = [ulong]; Value = $false; ExpectedResult = 0ul }
@{ Type = [decimal]; Value = $true; ExpectedResult = 1d }
@{ Type = [decimal]; Value = $false; ExpectedResult = 0d }
@{ Type = [float]; Value = $true; ExpectedResult = [float]1 }
@{ Type = [float]; Value = $false; ExpectedResult = [float]0 }
@{ Type = [double]; Value = $true; ExpectedResult = 1.0 }
@{ Type = [double]; Value = $false; ExpectedResult = 0.0 }
@{ Type = [bigint]; Value = $true; ExpectedResult = 1n }
@{ Type = [bigint]; Value = $false; ExpectedResult = 0n }
)
}
It 'should correctly convert $null to <Type> as <ExpectedResult>' -TestCases $NullToNumeral {
param($Type, $ExpectedResult)
$result = $null -as $Type
$result | Should -Be $ExpectedResult
$result | Should -BeOfType $Type
}
It 'should correctly convert <Value> to <Type> as <ExpectedResult>' -TestCases $BoolToNumeral {
param($Type, $Value, $ExpectedResult)
$result = $Value -as $Type
$result | Should -Be $ExpectedResult
$result | Should -BeOfType $Type
}
}