diff --git a/src/Peachpie.Runtime/Dynamic/BinderHelpers.cs b/src/Peachpie.Runtime/Dynamic/BinderHelpers.cs index 4bdc314d47..7f904606c6 100644 --- a/src/Peachpie.Runtime/Dynamic/BinderHelpers.cs +++ b/src/Peachpie.Runtime/Dynamic/BinderHelpers.cs @@ -19,10 +19,34 @@ namespace Pchp.Core.Dynamic [DebuggerNonUserCode] internal static class BinderHelpers { - public static bool IsParamsParameter(this ParameterInfo p) + public static bool IsParamsParameter(this ParameterInfo p, out Type elementType) { - //TODO: [ParamCollectionAttribute] Span - return p.ParameterType.IsArray && p.CustomAttributes.Any(attr => attr.AttributeType == typeof(ParamArrayAttribute)); + if (p.ParameterType.IsArray) + { + // [ParamArray] T[] + if (p.CustomAttributes.Any(attr => attr.AttributeType == typeof(ParamArrayAttribute))) + { + elementType = p.ParameterType.GetElementType(); + return true; + } + } + else if (p.ParameterType.IsConstructedGenericType && p.CustomAttributes.Any(attr => attr.AttributeType.Name == "ParamCollectionAttribute")) // [ParamCollectionAttribute] Span + { + var def_args = p.ParameterType.GenericTypeArguments; + if (def_args.Length == 1) + { + var def = p.ParameterType.GetGenericTypeDefinition(); + if (def == typeof(Span<>) || def == typeof(ReadOnlySpan<>)) + { + elementType = def_args[0]; // T + return true; + } + } + } + + // + elementType = null; + return false; } /// @@ -201,7 +225,7 @@ public static bool IsMandatoryParameter(this ParameterInfo p) !p.HasDefaultValue && // CLR default value !p.IsOptional && // has [Optional} attribute p.GetCustomAttribute() == null && // has [DefaultValue] attribute - !p.IsParamsParameter(); // is params + !p.IsParamsParameter(out _); // is params } /// @@ -1278,9 +1302,9 @@ public static Expression BindToCall(Expression instance, MethodBase method, Expr // regular parameter: - if (i == ps.Length - 1 && p.IsParamsParameter()) + if (i == ps.Length - 1 && p.IsParamsParameter(out var elementType)) { - boundargs[i] = args.BindParams(argi, p.ParameterType.GetElementType()); + boundargs[i] = Expression.Convert(args.BindParamsArray(argi, elementType), p.ParameterType); } else { diff --git a/src/Peachpie.Runtime/Dynamic/CallBinder.cs b/src/Peachpie.Runtime/Dynamic/CallBinder.cs index 6620daee86..c6e23b7eab 100644 --- a/src/Peachpie.Runtime/Dynamic/CallBinder.cs +++ b/src/Peachpie.Runtime/Dynamic/CallBinder.cs @@ -69,7 +69,7 @@ protected static bool IsClrMagicCallWithParams(MethodInfo method) if (!method.DeclaringType.GetPhpTypeInfo().IsPhpType) // only methods declared outside PHP code { if (count > 2) return true; - if (ps.Last().IsParamsParameter()) return true; + if (ps.Last().IsParamsParameter(out _)) return true; } } diff --git a/src/Peachpie.Runtime/Dynamic/OverloadBinder.cs b/src/Peachpie.Runtime/Dynamic/OverloadBinder.cs index 8662033f12..ff8b0714e6 100644 --- a/src/Peachpie.Runtime/Dynamic/OverloadBinder.cs +++ b/src/Peachpie.Runtime/Dynamic/OverloadBinder.cs @@ -287,7 +287,7 @@ protected ArgumentsBinder(Expression ctx) /// /// Bind arguments to array of parameters. /// - public abstract Expression BindParams(int fromarg, Type element_type); + public abstract Expression BindParamsArray(int fromarg, Type element_type); /// /// Gets expression representing cost of argument binding operation. @@ -563,7 +563,7 @@ public override Expression BindWriteBack(int targetarg, Expression expression) return Expression.Assign(element, ConvertExpression.Bind(expression, element.Type, _ctx)); } - public override Expression BindParams(int fromarg, Type element_type) + public override Expression BindParamsArray(int fromarg, Type element_type) { //if (element_type == _argsarray.Type.GetElementType()) //if (true) @@ -778,7 +778,7 @@ public override Expression BindWriteBack(int targetarg, Expression expression) return BinderHelpers.BindAssign(arg, expression, _ctx); } - public override Expression BindParams(int fromarg, Type element_type) + public override Expression BindParamsArray(int fromarg, Type element_type) { var count = _args.Length - fromarg; @@ -862,12 +862,10 @@ static Expression BindCostOf(MethodBase method, ArgumentsBinder args, BitArray c for (int im = 0; im < nmandatory + noptional; im++) { var p = ps[nimplicit + im]; - if (noptional != 0 && p.Position == ps.Length - 1 && p.IsParamsParameter()) + if (noptional != 0 && p.Position == ps.Length - 1 && p.IsParamsParameter(out var element_type)) { hasparams = true; - var element_type = p.ParameterType.GetElementType(); - // for (int o = io + nmandatory; o < argc; o++) result |= CostOf(argv[o], p.ElementType) if (argc_opt.HasValue) {