зеркало из https://github.com/microsoft/Power-Fx.git
Add IsDelegatable and IsPageable flags to UDF class (#2459)
These flags will allow the binder to mark a UDF as delegatable/pageable correctly based on the rule bound to it.
This commit is contained in:
Родитель
7c7e66bcea
Коммит
752efe46c4
|
@ -654,7 +654,7 @@ namespace Microsoft.PowerFx.Core.Binding
|
|||
return false;
|
||||
}
|
||||
|
||||
var isServerDelegatable = function.IsServerDelegatable(node, this);
|
||||
var isServerDelegatable = (function is not UserDefinedFunction udf || udf.Binding != null) && function.IsServerDelegatable(node, this);
|
||||
LogTelemetryForFunction(function, node, this, isServerDelegatable);
|
||||
return isServerDelegatable;
|
||||
}
|
||||
|
@ -1194,7 +1194,7 @@ namespace Microsoft.PowerFx.Core.Binding
|
|||
Contracts.AssertValue(func);
|
||||
|
||||
// Server delegatable call always returns a pageable object.
|
||||
if (func.SupportsPaging(node, this))
|
||||
if ((func is not UserDefinedFunction udf || udf.Binding != null) && func.SupportsPaging(node, this))
|
||||
{
|
||||
_isPageable.Set(node.Id, true);
|
||||
}
|
||||
|
@ -2261,7 +2261,7 @@ namespace Microsoft.PowerFx.Core.Binding
|
|||
if (function != null)
|
||||
{
|
||||
// If the invocation is async then the whole call path is async.
|
||||
if (markIfAsync && function.IsAsyncInvocation(node, this))
|
||||
if (markIfAsync && (function is UserDefinedFunction udf && udf.Binding != null) && function.IsAsyncInvocation(node, this))
|
||||
{
|
||||
FlagPathAsAsync(node);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.PowerFx.Core.App;
|
||||
|
@ -11,6 +12,7 @@ using Microsoft.PowerFx.Core.Binding;
|
|||
using Microsoft.PowerFx.Core.Binding.BindInfo;
|
||||
using Microsoft.PowerFx.Core.Entities;
|
||||
using Microsoft.PowerFx.Core.Errors;
|
||||
using Microsoft.PowerFx.Core.Functions.FunctionArgValidators;
|
||||
using Microsoft.PowerFx.Core.Glue;
|
||||
using Microsoft.PowerFx.Core.IR;
|
||||
using Microsoft.PowerFx.Core.IR.Nodes;
|
||||
|
@ -23,6 +25,7 @@ using Microsoft.PowerFx.Core.Utils;
|
|||
using Microsoft.PowerFx.Syntax;
|
||||
using Microsoft.PowerFx.Types;
|
||||
using static Microsoft.PowerFx.Core.Localization.TexlStrings;
|
||||
using CallNode = Microsoft.PowerFx.Syntax.CallNode;
|
||||
|
||||
namespace Microsoft.PowerFx.Core.Functions
|
||||
{
|
||||
|
@ -31,22 +34,42 @@ namespace Microsoft.PowerFx.Core.Functions
|
|||
/// This includings the binding (and hence IR for evaluation) -
|
||||
/// This is conceptually immutable after initialization - if the body or signature changes, you need to create a new instance.
|
||||
/// </summary>
|
||||
internal class UserDefinedFunction : TexlFunction
|
||||
internal class UserDefinedFunction : TexlFunction, IExternalPageableSymbol, IExternalDelegatableSymbol
|
||||
{
|
||||
private readonly bool _isImperative;
|
||||
private readonly IEnumerable<UDFArg> _args;
|
||||
private TexlBinding _binding;
|
||||
|
||||
public override bool IsAsync => _binding?.IsAsync(UdfBody) ?? false;
|
||||
public override bool IsAsync => _binding.IsAsync(UdfBody);
|
||||
|
||||
public bool IsPageable => _binding.IsPageable(_binding.Top);
|
||||
|
||||
public bool IsDelegatable => _binding.IsDelegatable(_binding.Top);
|
||||
|
||||
public override bool IsServerDelegatable(CallNode callNode, TexlBinding binding)
|
||||
{
|
||||
Contracts.AssertValue(callNode);
|
||||
Contracts.AssertValue(binding);
|
||||
Contracts.Assert(binding.GetInfo(callNode).Function is UserDefinedFunction udf && udf.Binding != null);
|
||||
|
||||
return base.IsServerDelegatable(callNode, binding) || IsDelegatable;
|
||||
}
|
||||
|
||||
public override bool SupportsParamCoercion => true;
|
||||
|
||||
private const int MaxParameterCount = 30;
|
||||
|
||||
private const int MaxParameterCount = 30;
|
||||
|
||||
public TexlNode UdfBody { get; }
|
||||
|
||||
public override bool IsSelfContained => !_isImperative;
|
||||
|
||||
public TexlBinding Binding => _binding;
|
||||
|
||||
public bool TryGetExternalDataSource(out IExternalDataSource dataSource)
|
||||
{
|
||||
return ArgValidators.DelegatableDataSourceInfoValidator.TryGetValidValue(_binding.Top, _binding, out dataSource);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UserDefinedFunction"/> class.
|
||||
/// </summary>
|
||||
|
@ -224,12 +247,12 @@ namespace Microsoft.PowerFx.Core.Functions
|
|||
{
|
||||
errors.Add(new TexlError(udf.Ident, DocumentErrorSeverity.Severe, TexlStrings.ErrUDF_FunctionNameRestricted, udfName));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (udf.Args.Count > MaxParameterCount)
|
||||
{
|
||||
errors.Add(new TexlError(udf.Ident, DocumentErrorSeverity.Severe, TexlStrings.ErrUDF_TooManyParameters, udfName, MaxParameterCount));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (udf.Args.Count > MaxParameterCount)
|
||||
{
|
||||
errors.Add(new TexlError(udf.Ident, DocumentErrorSeverity.Severe, TexlStrings.ErrUDF_TooManyParameters, udfName, MaxParameterCount));
|
||||
continue;
|
||||
}
|
||||
|
||||
var parametersOk = CheckParameters(udf.Args, errors, nameResolver, out var parameterTypes);
|
||||
|
@ -237,11 +260,11 @@ namespace Microsoft.PowerFx.Core.Functions
|
|||
if (!parametersOk || !returnTypeOk)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nameResolver.Functions.WithName(udfName).Any())
|
||||
{
|
||||
errors.Add(new TexlError(udf.Ident, DocumentErrorSeverity.Warning, TexlStrings.WrnUDF_ShadowingBuiltInFunction, udfName));
|
||||
}
|
||||
|
||||
if (nameResolver.Functions.WithName(udfName).Any())
|
||||
{
|
||||
errors.Add(new TexlError(udf.Ident, DocumentErrorSeverity.Warning, TexlStrings.WrnUDF_ShadowingBuiltInFunction, udfName));
|
||||
}
|
||||
|
||||
var func = new UserDefinedFunction(udfName.Value, returnType, udf.Body, udf.IsImperative, udf.Args, parameterTypes);
|
||||
|
|
|
@ -9,6 +9,7 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.PowerFx.Core;
|
||||
using Microsoft.PowerFx.Core.Functions;
|
||||
using Microsoft.PowerFx.Core.IR;
|
||||
using Microsoft.PowerFx.Core.Localization;
|
||||
using Microsoft.PowerFx.Core.Tests;
|
||||
|
@ -673,6 +674,39 @@ namespace Microsoft.PowerFx.Tests
|
|||
Assert.True(expectedError, ex.Message);
|
||||
Assert.Contains(expectedMethodFailure, ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
public void DelegatableUDFTest()
|
||||
{
|
||||
var config = new PowerFxConfig();
|
||||
config.EnableSetFunction();
|
||||
|
||||
var schema = DType.CreateTable(
|
||||
new TypedName(DType.Guid, new DName("ID")),
|
||||
new TypedName(DType.Number, new DName("Value")));
|
||||
config.SymbolTable.AddEntity(new TestDataSource("MyDataSource", schema));
|
||||
config.SymbolTable.AddType(new DName("MyDataSourceTableType"), FormulaType.Build(schema));
|
||||
|
||||
var recalcEngine = new RecalcEngine(config);
|
||||
|
||||
recalcEngine.AddUserDefinedFunction("A():MyDataSourceTableType = Filter(Sort(MyDataSource,Value), Value > 10);C():MyDataSourceTableType = A();", CultureInfo.InvariantCulture, symbolTable: recalcEngine.EngineSymbols, allowSideEffects: true);
|
||||
var func = recalcEngine.Functions.WithName("A");
|
||||
|
||||
if (func is UserDefinedFunction udf)
|
||||
{
|
||||
Assert.True(udf.IsAsync);
|
||||
Assert.True(udf.IsDelegatable);
|
||||
}
|
||||
|
||||
func = recalcEngine.Functions.WithName("C");
|
||||
|
||||
if (func is UserDefinedFunction udf2)
|
||||
{
|
||||
Assert.True(udf2.IsAsync);
|
||||
Assert.True(udf2.IsDelegatable);
|
||||
}
|
||||
}
|
||||
|
||||
// Binding to inner functions does not impact outer functions.
|
||||
|
|
Загрузка…
Ссылка в новой задаче