Skip to content
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

F#-based library (Deedle) fails in PS #21372

Open
5 tasks done
eosfor opened this issue Mar 25, 2024 · 3 comments · May be fixed by #21387
Open
5 tasks done

F#-based library (Deedle) fails in PS #21372

eosfor opened this issue Mar 25, 2024 · 3 comments · May be fixed by #21387
Labels
In-PR Indicates that a PR is out for the issue Issue-Bug Issue has been identified as a bug in the product Needs-Triage The issue is new and needs to be triaged by a work group.

Comments

@eosfor
Copy link

eosfor commented Mar 25, 2024

Prerequisites

Steps to reproduce

I'm trying to use Deedle to work with various data

install-package -Name deedle -scope CurrentUser

$fsharp = add-type -LiteralPath 'C:\Users\<someUser>\AppData\Local\PackageManagement\NuGet\Packages\FSharp.Core.8.0.200\lib\netstandard2.0\FSharp.Core.dll' -PassThru
$deedle = add-type -LiteralPath 'C:\Users\<someUser>\AppData\Local\PackageManagement\NuGet\Packages\Deedle.3.0.0\lib\netstandard2.0\Deedle.dll' -PassThru

[Deedle.Frame]::ReadCsv("C:\temp\charts\locations.csv")

Expected behavior

should not crash

Actual behavior

crashes as follows


OperationStopped: Expression of type 'System.Reflection.Missing' cannot be used for parameter of type 'System.Nullable`1[System.Boolean]' of method 'Deedle.Frame`2[System.Int32,System.String] ReadCsv(System.String, System.Nullable`1[System.Boolean], System.Nullable`1[System.Boolean], System.Nullable`1[System.Int32], System.String, System.String, System.String, System.Nullable`1[System.Int32], System.String[], Boolean)' (Parameter 'arguments[1]')

Error details

PS C:\temp> get-error

Exception             :
    Type       : System.ArgumentException
    Message    : Expression of type 'System.Reflection.Missing' cannot be used for parameter of type 'System.Nullable`1[System.Boolean]' of method 'Deedle.Frame`2[System.Int32,System.String] ReadCsv(System.String,
System.Nullable`1[System.Boolean], System.Nullable`1[System.Boolean], System.Nullable`1[System.Int32], System.String, System.String, System.String, System.Nullable`1[System.Int32], System.String[], Boolean)' (Parameter
'arguments[1]')
    ParamName  : arguments[1]
    TargetSite :
        Name          : ValidateOneArgument
        DeclaringType : System.Dynamic.Utils.ExpressionUtils, System.Linq.Expressions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
        MemberType    : Method
        Module        : System.Linq.Expressions.dll
    Data       : System.Collections.ListDictionaryInternal
    Source     : System.Linq.Expressions
    HResult    : -2147024809
    StackTrace :
   at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, String methodParamName, String argumentParamName, Int32 index)
   at System.Dynamic.Utils.ExpressionUtils.ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ReadOnlyCollection`1& arguments, String methodParamName)
   at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments)
   at System.Management.Automation.Language.PSInvokeMemberBinder.InvokeMethod(MethodBase mi, DynamicMetaObject target, DynamicMetaObject[] args, Boolean expandParameters, MethodInvocationType invocationType)
   at System.Management.Automation.Language.PSInvokeMemberBinder.InvokeDotNetMethod(CallInfo callInfo, String name, PSMethodInvocationConstraints psMethodInvocationConstraints, MethodInvocationType methodInvocationType,
DynamicMetaObject target, DynamicMetaObject[] args, BindingRestrictions restrictions, MethodInformation[] mi, Type errorExceptionType)
   at System.Management.Automation.Language.PSInvokeMemberBinder.FallbackInvokeMember(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion)
   at System.Dynamic.DynamicMetaObjectBinder.Bind(Object[] args, ReadOnlyCollection`1 parameters, LabelTarget returnLabel)
   at System.Runtime.CompilerServices.CallSiteBinder.BindCore[T](CallSite`1 site, Object[] args)
   at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
   at System.Management.Automation.Interpreter.DynamicInstruction`3.Run(InterpretedFrame frame)
   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
CategoryInfo          : OperationStopped: (:) [], ArgumentException
FullyQualifiedErrorId : System.ArgumentException
InvocationInfo        :
    ScriptLineNumber : 1
    OffsetInLine     : 1
    HistoryId        : 4
    Line             : [Deedle.Frame]::ReadCsv("C:\temp\charts\locations.csv")
    Statement        : [Deedle.Frame]::ReadCsv("C:\temp\charts\locations.csv")
    PositionMessage  : At line:1 char:1
                       + [Deedle.Frame]::ReadCsv("C:\temp\charts\locations.csv")
                       + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 1

Environment data

PS C:\temp> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.4.1
PSEdition                      Core
GitCommitId                    7.4.1
OS                             Microsoft Windows 10.0.22631
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

image

@eosfor eosfor added the Needs-Triage The issue is new and needs to be triaged by a work group. label Mar 25, 2024
@jborean93
Copy link
Collaborator

It looks like PowerShell has an issue invoking methods with an [Optional] argument which based on the disassembly of the Deedle assembly is the case for ReadCsv

image

It tries to call the method with [Type]::Missing in replacement of the value causing the problem. A way to replicate this without an external assembly is

Add-Type -TypeDefinition @'
using System;
using System.Runtime.InteropServices;

public class MyClass
{
    public static int TestMethod1(int defaultValue = 1) => defaultValue;
    public static int TestMethod2([Optional] int defaultValue) => defaultValue;
    public static int TestMethod1Implicit() => TestMethod1();
    public static int TestMethod2Implicit() => TestMethod2();
}
'@

[MyClass]::TestMethod1()  # works as pwsh supports default parameter values
[MyClass]::TestMethod2()  # fails due to the issue you are seeing

# What pwsh is doing internally when it comes to default values and [Optional]
# Works with a default value
[MyClass].GetMethod('TestMethod1').Invoke($null, @([Type]::Missing))
# Fails with an [Optional] value
[MyClass].GetMethod('TestMethod2').Invoke($null, @([Type]::Missing))

We would have to find out how to invoke such a method to use the default for that type using reflection so that pwsh can also do so.

@jborean93
Copy link
Collaborator

I've opened dotnet/runtime#100322 because it seems like this should potentially be fixed in .NET rather than PowerShell. In case they think it's not a bug and aren't wanting to fix it this diff when applied locally fixed PowerShell to work for methods with such attributes

diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs
index 3e3ec3fbc..62814ed77 100644
--- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs
+++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs
@@ -7163,7 +7163,7 @@ namespace System.Management.Automation.Language
                     Diagnostics.Assert(parameters[i].IsOptional,
                         "if there are too few arguments, FindBestMethod should only succeed if parameters are optional");
                     var argValue = parameters[i].DefaultValue;
-                    if (argValue == null)
+                    if (argValue == null || (!parameters[i].HasDefaultValue && argValue == Type.Missing))
                     {
                         argExprs[i] = Expression.Default(parameterType);
                     }

I need to do some more in depth testing to ensure it works in other scenarios but I think it should be fine.

@jborean93 jborean93 linked a pull request Mar 27, 2024 that will close this issue
22 tasks
@jborean93
Copy link
Collaborator

.NET has said the behaviour is expected so I've opened #21387 to fix up PowerShell to call it with the default argument value for that type.

@StevenBucher98 StevenBucher98 added Issue-Bug Issue has been identified as a bug in the product In-PR Indicates that a PR is out for the issue labels Apr 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In-PR Indicates that a PR is out for the issue Issue-Bug Issue has been identified as a bug in the product Needs-Triage The issue is new and needs to be triaged by a work group.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants