Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/libraries/Microsoft.PowerFx.Core/Binding/BindKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,16 @@ internal enum BindKind

Lim,
}

internal static class BindKindExtensions
{
internal static bool IsValidInSimpleExpression(this BindKind kind)
{
// Starting simple, we could expand this set over time.
return kind == BindKind.ThisItem ||
kind == BindKind.Enum ||
kind == BindKind.LambdaField ||
kind == BindKind.LambdaFullRecord;
}
}
}
22 changes: 18 additions & 4 deletions src/libraries/Microsoft.PowerFx.Core/Binding/Binder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1061,8 +1061,7 @@ internal bool TryGetDataSourceInfo(TexlNode node, out IExternalDataSource dataSo
return dataSourceInfo != null;

case NodeKind.DottedName:
IExpandInfo info;
if (TryGetEntityInfo(node.AsDottedName(), out info))
if (TryGetEntityInfo(node.AsDottedName(), out IExpandInfo info))
{
dataSourceInfo = info.ParentDataSource;
return dataSourceInfo != null;
Expand Down Expand Up @@ -2897,6 +2896,16 @@ public override void Visit(FirstNameNode node)
_txb.SetType(node, DType.Error);
_txb.SetInfo(node, FirstNameInfo.Create(node, default(NameLookupInfo)));
return;
}

// We have an allowlist of kinds permitted in simple expressions, all of which should be Sync.
// The IsAsync check is just to be sure we're not introducing async
// if things are added to the set of valid kinds in the future.
// As the main point of the "Simple Expression" constraint is to ensure certain expressions are sync
// but that's harder to communicate to low-code users.
if (_txb.BindingConfig.EnforceSimpleExpressionConstraint && (!lookupInfo.Kind.IsValidInSimpleExpression() || lookupInfo.IsAsync))
{
_txb.ErrorContainer.Error(node, TexlStrings.ErrViolatedSimpleConstraintAccess, node.Ident.Name.Value);
}

var isConstantNamedFormula = false;
Expand Down Expand Up @@ -4849,9 +4858,14 @@ private void FinalizeCall(CallNode node)
else if (func.IsTestOnly && _txb.Property != null && !_txb.Property.IsTestCaseProperty)
{
_txb.ErrorContainer.EnsureError(node, TexlStrings.ErrTestPropertyExpected);
}
else if ((!func.IsAllowedInSimpleExpressions || _txb.IsAsync(node)) && _txb.BindingConfig.EnforceSimpleExpressionConstraint)
{
// Functions that are not allowed in simple expressions cannot be used when the binding config restricts to simple expressions.
_txb.ErrorContainer.EnsureError(node, TexlStrings.ErrViolatedSimpleConstraintFunction, func.Name);
}

// Auto-refreshable functions cannot be used in behavior rules.
// Auto-refreshable functions cannot be used in behavior rules.
else if (func.IsAutoRefreshable && _txb.BindingConfig.AllowsSideEffects)
{
_txb.ErrorContainer.EnsureError(node, TexlStrings.ErrAutoRefreshNotAllowed);
Expand Down
24 changes: 20 additions & 4 deletions src/libraries/Microsoft.PowerFx.Core/Binding/BindingConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,31 @@ internal class BindingConfig

public bool MarkAsAsyncOnLazilyLoadedControlRef { get; } = false;

public bool UserDefinitionsMode { get; }
public bool UserDefinitionsMode { get; }

/// <summary>
/// Enforces the expression must be "Simple", i.e. no async, no global access, no impure functions
/// This is mainly used for controls like combobox where the expression is used to populate a dropdown
/// and must be FAST to run on large datasets.
/// </summary>
internal bool EnforceSimpleExpressionConstraint { get; }

public BindingConfig(bool allowsSideEffects = false, bool useThisRecordForRuleScope = false, bool numberIsFloat = false, bool analysisMode = false, bool markAsAsyncOnLazilyLoadedControlRef = false, bool userDefinitionsMode = false)
public BindingConfig(
bool allowsSideEffects = false,
bool useThisRecordForRuleScope = false,
bool numberIsFloat = false,
bool analysisMode = false,
bool markAsAsyncOnLazilyLoadedControlRef = false,
bool userDefinitionsMode = false,
bool enforceSimpleExpressions = false)
{
AllowsSideEffects = allowsSideEffects;
UseThisRecordForRuleScope = useThisRecordForRuleScope;
NumberIsFloat = numberIsFloat;
AnalysisMode = analysisMode;
MarkAsAsyncOnLazilyLoadedControlRef = markAsAsyncOnLazilyLoadedControlRef;
UserDefinitionsMode = userDefinitionsMode;
UserDefinitionsMode = userDefinitionsMode;
EnforceSimpleExpressionConstraint = enforceSimpleExpressions;
}

public BindingConfig Clone(bool allowsSideEffects)
Expand All @@ -45,7 +60,8 @@ public BindingConfig Clone(bool allowsSideEffects)
numberIsFloat: this.NumberIsFloat,
analysisMode: this.AnalysisMode,
markAsAsyncOnLazilyLoadedControlRef: this.MarkAsAsyncOnLazilyLoadedControlRef,
userDefinitionsMode: this.UserDefinitionsMode);
userDefinitionsMode: this.UserDefinitionsMode,
enforceSimpleExpressions: this.EnforceSimpleExpressionConstraint);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ internal abstract class TexlFunction : IFunction
// abstract will force them to do so.
public abstract bool IsSelfContained { get; }

/// <summary>
/// Allowlist for functions that can be used in "simple" expressions.
/// </summary>
public virtual bool IsAllowedInSimpleExpressions => false;

// Return true if the function is stateless (same result for same input), or false otherwise.
public virtual bool IsStateless => true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -931,5 +931,7 @@ internal static class TexlStrings
public static ErrorResourceKey ErrJoinArgIsNotAsNode = new ErrorResourceKey("ErrJoinArgIsNotAsNode");
public static ErrorResourceKey ErrJoinAtLeastOneRigthRecordField = new ErrorResourceKey("ErrJoinAtLeastOneRigthRecordField");
public static ErrorResourceKey ErrJoinDottedNameleft = new ErrorResourceKey("ErrJoinDottedNameleft");
public static ErrorResourceKey ErrViolatedSimpleConstraintAccess = new ErrorResourceKey("ErrViolatedSimpleConstraintAccess");
public static ErrorResourceKey ErrViolatedSimpleConstraintFunction = new ErrorResourceKey("ErrViolatedSimpleConstraintFunction");
}
}
4 changes: 2 additions & 2 deletions src/libraries/Microsoft.PowerFx.Core/Public/CheckResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -766,8 +766,8 @@ public IEnumerable<string> GetFunctionNames(bool anonymizeUnknownPublicFunctions
// for error reporting.
internal interface IOperationStatus
{
public IEnumerable<ExpressionError> Errors { get; }
IEnumerable<ExpressionError> Errors { get; }

public bool IsSuccess { get; }
bool IsSuccess { get; }
}
}
24 changes: 18 additions & 6 deletions src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Boolean.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ internal sealed class BooleanFunction : BuiltinFunction

public override bool IsSelfContained => true;

public override bool SupportsParamCoercion => false;
public override bool SupportsParamCoercion => false;

public override bool IsAllowedInSimpleExpressions => true;

public BooleanFunction()
: base(BooleanInvariantFunctionName, TexlStrings.AboutBoolean, FunctionCategories.Text, DType.Boolean, 0, 1, 1, DType.String)
Expand Down Expand Up @@ -86,7 +88,9 @@ internal sealed class BooleanNFunction : BuiltinFunction
{
public override bool IsSelfContained => true;

public override bool SupportsParamCoercion => false;
public override bool SupportsParamCoercion => false;

public override bool IsAllowedInSimpleExpressions => true;

public BooleanNFunction()
: base(BooleanFunction.BooleanInvariantFunctionName, TexlStrings.AboutBooleanN, FunctionCategories.Text, DType.Boolean, 0, 1, 1, DType.Number)
Expand Down Expand Up @@ -153,7 +157,9 @@ internal sealed class BooleanWFunction : BuiltinFunction
{
public override bool IsSelfContained => true;

public override bool SupportsParamCoercion => false;
public override bool SupportsParamCoercion => false;

public override bool IsAllowedInSimpleExpressions => true;

// Reusing BooleanN strings as they are generic for numbers
public BooleanWFunction()
Expand Down Expand Up @@ -225,7 +231,9 @@ internal sealed class BooleanBFunction : BuiltinFunction
{
public override bool IsSelfContained => true;

public override bool SupportsParamCoercion => false;
public override bool SupportsParamCoercion => false;

public override bool IsAllowedInSimpleExpressions => true;

public BooleanBFunction()
: base(BooleanFunction.BooleanInvariantFunctionName, TexlStrings.AboutBooleanB, FunctionCategories.Text, DType.Boolean, 0, 1, 1, DType.Boolean)
Expand Down Expand Up @@ -326,7 +334,9 @@ internal sealed class BooleanLFunction : BuiltinFunction
{
public override bool IsSelfContained => true;

public override bool SupportsParamCoercion => false;
public override bool SupportsParamCoercion => false;

public override bool IsAllowedInSimpleExpressions => true;

public BooleanLFunction()
: base(BooleanFunction.BooleanInvariantFunctionName, TexlStrings.AboutBooleanL, FunctionCategories.Text, DType.Boolean, 0, 1, 1, DType.OptionSetValue)
Expand Down Expand Up @@ -411,7 +421,9 @@ internal sealed class BooleanFunction_UO : BuiltinFunction
{
public override bool IsSelfContained => true;

public override bool SupportsParamCoercion => false;
public override bool SupportsParamCoercion => false;

public override bool IsAllowedInSimpleExpressions => true;

public BooleanFunction_UO()
: base(BooleanFunction.BooleanInvariantFunctionName, TexlStrings.AboutBoolean, FunctionCategories.Text, DType.Boolean, 0, 1, 1, DType.UntypedObject)
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Char.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public override ArgPreprocessor GetArgPreprocessor(int index, int argCount)

public override bool IsSelfContained => true;

public override bool IsAllowedInSimpleExpressions => true;

public CharFunction()
: base("Char", TexlStrings.AboutChar, FunctionCategories.Text, DType.String, 0, 1, 1, DType.Number)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ internal sealed class ConcatenateFunction : BuiltinFunction
{
public override bool IsSelfContained => true;

public override bool IsAllowedInSimpleExpressions => true;

public ConcatenateFunction()
: base("Concatenate", TexlStrings.AboutConcatenate, FunctionCategories.Text, DType.String, 0, 1, int.MaxValue)
{
Expand Down
48 changes: 36 additions & 12 deletions src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/DateTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ public override ArgPreprocessor GetArgPreprocessor(int index, int argCount)

public override bool IsSelfContained => true;

public override bool HasPreciseErrors => true;
public override bool HasPreciseErrors => true;

public override bool IsAllowedInSimpleExpressions => true;

public DateFunction()
: base("Date", TexlStrings.AboutDate, FunctionCategories.DateTime, DType.Date, 0, 3, 3, DType.Number, DType.Number, DType.Number)
Expand All @@ -46,7 +48,9 @@ internal abstract class ExtractDateTimeFunctionBase : BuiltinFunction
{
public override bool HasPreciseErrors => true;

public override bool IsSelfContained => true;
public override bool IsSelfContained => true;

public override bool IsAllowedInSimpleExpressions => true;

public ExtractDateTimeFunctionBase(string name, TexlStrings.StringGetter description)
: base(name, description, FunctionCategories.DateTime, DType.Number, 0, 1, 1, DType.DateTime)
Expand Down Expand Up @@ -86,7 +90,9 @@ public override ArgPreprocessor GetArgPreprocessor(int index, int argCount)

public override bool IsSelfContained => true;

public override bool HasPreciseErrors => true;
public override bool HasPreciseErrors => true;

public override bool IsAllowedInSimpleExpressions => true;

public TimeFunction()
: base("Time", TexlStrings.AboutTime, FunctionCategories.DateTime, DType.Time, 0, 3, 4, DType.Number, DType.Number, DType.Number, DType.Number)
Expand All @@ -110,7 +116,9 @@ public override ArgPreprocessor GetArgPreprocessor(int index, int argCount)

public override bool IsSelfContained => true;

public override bool HasPreciseErrors => true;
public override bool HasPreciseErrors => true;

public override bool IsAllowedInSimpleExpressions => true;

public DateTimeFunction()
: base("DateTime", TexlStrings.AboutDateTime, FunctionCategories.DateTime, DType.DateTime, 0, 6, 7, DType.Number, DType.Number, DType.Number, DType.Number, DType.Number, DType.Number, DType.Number)
Expand Down Expand Up @@ -232,7 +240,9 @@ internal sealed class WeekdayFunction : BuiltinFunction
{
public override bool IsSelfContained => true;

public override bool HasPreciseErrors => true;
public override bool HasPreciseErrors => true;

public override bool IsAllowedInSimpleExpressions => true;

public WeekdayFunction()
: base("Weekday", TexlStrings.AboutWeekday, FunctionCategories.DateTime, DType.Number, 0, 1, 2, DType.DateTime, BuiltInEnums.StartOfWeekEnum.FormulaType._type)
Expand Down Expand Up @@ -269,7 +279,9 @@ internal sealed class WeekNumFunction : BuiltinFunction
{
public override bool IsSelfContained => true;

public override bool HasPreciseErrors => true;
public override bool HasPreciseErrors => true;

public override bool IsAllowedInSimpleExpressions => true;

public WeekNumFunction()
: base("WeekNum", TexlStrings.AboutWeekNum, FunctionCategories.DateTime, DType.Number, 0, 1, 2, DType.DateTime, BuiltInEnums.StartOfWeekEnum.FormulaType._type)
Expand Down Expand Up @@ -367,7 +379,9 @@ internal abstract class DateTimeGenericFunction : BuiltinFunction
{
public override bool IsSelfContained => true;

public override bool HasPreciseErrors => true;
public override bool HasPreciseErrors => true;

public override bool IsAllowedInSimpleExpressions => true;

protected DateTimeGenericFunction(string name, TexlStrings.StringGetter description, DType returnType)
: base(name, description, FunctionCategories.DateTime, returnType, 0, 1, 2, DType.String, DType.String)
Expand Down Expand Up @@ -445,7 +459,9 @@ public DateTimeValueFunction()
// DateAdd(timestamp: d, delta: n, [ unit: TimeUnits ]) : d
internal sealed class DateAddFunction : BuiltinFunction
{
public override bool IsSelfContained => true;
public override bool IsSelfContained => true;

public override bool IsAllowedInSimpleExpressions => true;

public DateAddFunction()
: base("DateAdd", TexlStrings.AboutDateAdd, FunctionCategories.DateTime, DType.DateTime, 0, 2, 3, DType.DateTime, DType.Number, BuiltInEnums.TimeUnitEnum.FormulaType._type)
Expand Down Expand Up @@ -627,7 +643,9 @@ internal sealed class DateDiffFunction : BuiltinFunction
{
public override bool IsSelfContained => true;

public override bool HasPreciseErrors => true;
public override bool HasPreciseErrors => true;

public override bool IsAllowedInSimpleExpressions => true;

public DateDiffFunction()
: base("DateDiff", TexlStrings.AboutDateDiff, FunctionCategories.DateTime, DType.Number, 0, 2, 3, DType.DateTime, DType.DateTime, BuiltInEnums.TimeUnitEnum.FormulaType._type)
Expand Down Expand Up @@ -784,7 +802,9 @@ internal sealed class DateValueFunction_UO : BuiltinFunction
{
public override bool HasPreciseErrors => true;

public override bool IsSelfContained => true;
public override bool IsSelfContained => true;

public override bool IsAllowedInSimpleExpressions => true;

public DateValueFunction_UO()
: base(DateValueFunction.DateValueInvariantFunctionName, TexlStrings.AboutDateValue, FunctionCategories.DateTime, DType.Date, 0, 1, 1, DType.UntypedObject)
Expand All @@ -802,7 +822,9 @@ internal sealed class TimeValueFunction_UO : BuiltinFunction
{
public override bool HasPreciseErrors => true;

public override bool IsSelfContained => true;
public override bool IsSelfContained => true;

public override bool IsAllowedInSimpleExpressions => true;

public TimeValueFunction_UO()
: base(TimeValueFunction.TimeValueFunctionInvariantName, TexlStrings.AboutTimeValue, FunctionCategories.DateTime, DType.Time, 0, 1, 1, DType.UntypedObject)
Expand All @@ -820,7 +842,9 @@ internal sealed class DateTimeValueFunction_UO : BuiltinFunction
{
public override bool HasPreciseErrors => true;

public override bool IsSelfContained => true;
public override bool IsSelfContained => true;

public override bool IsAllowedInSimpleExpressions => true;

public DateTimeValueFunction_UO()
: base(DateTimeValueFunction.DateTimeValueInvariantFunctionName, TexlStrings.AboutDateTimeValue, FunctionCategories.DateTime, DType.DateTime, 0, 1, 1, DType.UntypedObject)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public override ArgPreprocessor GetArgPreprocessor(int index, int argCount)

public override bool HasPreciseErrors => true;

public override bool IsAllowedInSimpleExpressions => true;

public Dec2HexFunction()
: base("Dec2Hex", TexlStrings.AboutDec2Hex, FunctionCategories.MathAndStat, DType.String, 0, 1, 2, DType.Number, DType.Number)
{
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Find.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ internal sealed class FindFunction : BuiltinFunction
{
public override bool IsSelfContained => true;

public override bool IsAllowedInSimpleExpressions => true;

public FindFunction()
: base("Find", TexlStrings.AboutFind, FunctionCategories.Text, DType.Number, 0, 2, 3, DType.String, DType.String, DType.Number)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ public override ArgPreprocessor GetArgPreprocessor(int index, int argCount)

public override bool IsSelfContained => true;

public override bool IsStateless => true;
public override bool IsStateless => true;

public override bool IsAllowedInSimpleExpressions => true;

public Hex2DecFunction()
: base("Hex2Dec", TexlStrings.AboutHex2Dec, FunctionCategories.MathAndStat, DType.Number, 0, 1, 1, DType.String)
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/If.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ internal sealed class IfFunction : BuiltinFunction

public override bool IsSelfContained => true;

public override bool IsAllowedInSimpleExpressions => true;

public IfFunction()
: base("If", TexlStrings.AboutIf, FunctionCategories.Logical, DType.Unknown, 0, 2, int.MaxValue)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ internal sealed class VariadicLogicalFunction : BuiltinFunction

public override bool IsSelfContained => true;

public override bool IsAllowedInSimpleExpressions => true;

internal readonly bool _isAnd;

public VariadicLogicalFunction(bool isAnd)
Expand Down
Loading