diff --git a/src/UiPath.Workflow.Runtime/Expressions/AddAssign.cs b/src/UiPath.Workflow.Runtime/Expressions/AddAssign.cs new file mode 100644 index 00000000..ca1261c0 --- /dev/null +++ b/src/UiPath.Workflow.Runtime/Expressions/AddAssign.cs @@ -0,0 +1,98 @@ +// This file is part of Core WF which is licensed under the MIT license. +// See LICENSE file in the project root for full license information. + +using System.Activities.Validation; +using System.ComponentModel; +using System.Linq.Expressions; +using System.Numerics; + +namespace System.Activities.Expressions +{ + /// + /// A code activity which incrementally assigns a numeral. + /// + /// + /// int myNum = 5; + /// myNum += 5; // 10. + /// + /// The operand to modify. + /// The operand with which to modify . + /// The resulting type of hte operation. + public sealed class AddAssign : CodeActivity +#if NET7_0_OR_GREATER + where TLeft : INumber + where TRight : INumber + where TResult : INumber +#endif + { + private static Func? checkedOperationFunction = null; + private static Func? uncheckedOperationFunction = null; + + /// + /// Gets or sets the left operand which will be modified by this operation. + /// + [RequiredArgument] + public InArgument Left { get; set; } = new(); + + /// + /// Gets or sets the right operand which will be the modifier for this operation. + /// + [RequiredArgument] + public InArgument Right { get; set; } = new(); + + /// + /// Gets or sets a value indicating whether this operation will happen in a checked or unchecked context. + /// + [DefaultValue(true)] + public bool IsChecked { get; set; } = true; + + /// + protected override void CacheMetadata(CodeActivityMetadata metadata) + { + BinaryExpressionHelper.OnGetArguments(metadata, Left, Right); + + if (IsChecked) + { + EnsureOperationFunction(metadata, ref checkedOperationFunction, ExpressionType.AddAssignChecked); + } + else + { + EnsureOperationFunction(metadata, ref uncheckedOperationFunction, ExpressionType.AddAssign); + } + } + + private static void EnsureOperationFunction + ( + CodeActivityMetadata metadata, + ref Func? operationFunction, + ExpressionType operatorType + ) + { + if (operationFunction == null) + { + if (!BinaryExpressionHelper.TryGenerateLinqDelegate( + operatorType, + out operationFunction, + out ValidationError? validationError)) + { + metadata.AddValidationError(validationError); + } + } + } + + /// + protected override TResult Execute(CodeActivityContext context) + { + TLeft leftValue = Left.Get(context); + TRight rightValue = Right.Get(context); + + // If user changed checked flag between open and execution, an NRE may be thrown. + // This is by design. + // Nullability check silenced because the corresponding value is guaranteed to be + // non-null + return IsChecked + ? checkedOperationFunction!(leftValue, rightValue) + : uncheckedOperationFunction!(leftValue, rightValue); + } + } +} diff --git a/src/UiPath.Workflow.Runtime/Expressions/BinaryExpressionHelper.cs b/src/UiPath.Workflow.Runtime/Expressions/BinaryExpressionHelper.cs index 8bec8c78..abc96584 100644 --- a/src/UiPath.Workflow.Runtime/Expressions/BinaryExpressionHelper.cs +++ b/src/UiPath.Workflow.Runtime/Expressions/BinaryExpressionHelper.cs @@ -8,8 +8,19 @@ namespace System.Activities.Expressions; -internal static class BinaryExpressionHelper +/// +/// Helpers for binary expressions. +/// +public static class BinaryExpressionHelper { + /// + /// Binds metadata when getting arguments. + /// + /// The type of the left argument. + /// The type of the right argument. + /// The metadata. + /// The left argument. + /// The right argument. public static void OnGetArguments(CodeActivityMetadata metadata, InArgument left, InArgument right) { RuntimeArgument rightArgument = new("Right", typeof(TRight), ArgumentDirection.In, true); @@ -26,6 +37,16 @@ internal static class BinaryExpressionHelper }); } + /// + /// Generates a delegate. + /// + /// The type of the left argument. + /// The type of the right argument. + /// The return type of the operation. + /// The type of expression. + /// The resulting . + /// If the operation failed, the error. + /// if the operation was successful; otherwise, . public static bool TryGenerateLinqDelegate(ExpressionType operatorType, out Func function, out ValidationError validationError) { function = null; diff --git a/src/UiPath.Workflow.Runtime/Expressions/Decrement.cs b/src/UiPath.Workflow.Runtime/Expressions/Decrement.cs new file mode 100644 index 00000000..c9a93dcf --- /dev/null +++ b/src/UiPath.Workflow.Runtime/Expressions/Decrement.cs @@ -0,0 +1,57 @@ +// This file is part of Core WF which is licensed under the MIT license. +// See LICENSE file in the project root for full license information. + +using System.Activities.Validation; +using System.Linq.Expressions; +using System.Numerics; + +namespace System.Activities.Expressions +{ + /// + /// A code activity which decrements a numeral. + /// + /// A numeric value, such as or . + public sealed class Decrement : CodeActivity +#if NET7_0_OR_GREATER + where TNumeral : IIncrementOperators +#endif + { + private static Func? operationFunction = null!; + + /// + /// Gets or sets the numeral value that will be incremented. + /// + [RequiredArgument] + public InOutArgument Numeral { get; set; } = new(); + + /// + protected override void CacheMetadata(CodeActivityMetadata metadata) + { + UnaryExpressionHelper.OnGetArguments(metadata, Numeral); + EnsureOperationFunction(metadata, ref operationFunction!, ExpressionType.Decrement); + } + + private static void EnsureOperationFunction + ( + CodeActivityMetadata metadata, + ref Func operationFunction, + ExpressionType operatorType + ) + { + if (operationFunction is null) + { + if (!UnaryExpressionHelper.TryGenerateLinqDelegate(operatorType, out operationFunction!, out ValidationError? validationError)) + { + metadata.AddValidationError(validationError); + } + } + } + + /// + protected override TNumeral Execute(CodeActivityContext context) + { + TNumeral value = Numeral.Get(context); + return operationFunction!.Invoke(value); + } + } +} diff --git a/src/UiPath.Workflow.Runtime/Expressions/Increment.cs b/src/UiPath.Workflow.Runtime/Expressions/Increment.cs new file mode 100644 index 00000000..5837056b --- /dev/null +++ b/src/UiPath.Workflow.Runtime/Expressions/Increment.cs @@ -0,0 +1,57 @@ +// This file is part of Core WF which is licensed under the MIT license. +// See LICENSE file in the project root for full license information. + +using System.Activities.Validation; +using System.Linq.Expressions; +using System.Numerics; + +namespace System.Activities.Expressions +{ + /// + /// A code activity which increments a numeral. + /// + /// A numeric value, such as or . + public sealed class Increment : CodeActivity +#if NET7_0_OR_GREATER + where TNumeral : IIncrementOperators +#endif + { + private static Func? operationFunction = null!; + + /// + /// Gets or sets the numeral value that will be incremented. + /// + [RequiredArgument] + public InOutArgument Numeral { get; set; } = new(); + + /// + protected override void CacheMetadata(CodeActivityMetadata metadata) + { + UnaryExpressionHelper.OnGetArguments(metadata, Numeral); + EnsureOperationFunction(metadata, ref operationFunction!, ExpressionType.Increment); + } + + private static void EnsureOperationFunction + ( + CodeActivityMetadata metadata, + ref Func operationFunction, + ExpressionType operatorType + ) + { + if (operationFunction is null) + { + if (!UnaryExpressionHelper.TryGenerateLinqDelegate(operatorType, out operationFunction!, out ValidationError? validationError)) + { + metadata.AddValidationError(validationError); + } + } + } + + /// + protected override TNumeral Execute(CodeActivityContext context) + { + TNumeral value = Numeral.Get(context); + return operationFunction!.Invoke(value); + } + } +} diff --git a/src/UiPath.Workflow.Runtime/Expressions/SubtractAssign.cs b/src/UiPath.Workflow.Runtime/Expressions/SubtractAssign.cs new file mode 100644 index 00000000..0fe57915 --- /dev/null +++ b/src/UiPath.Workflow.Runtime/Expressions/SubtractAssign.cs @@ -0,0 +1,98 @@ +// This file is part of Core WF which is licensed under the MIT license. +// See LICENSE file in the project root for full license information. + +using System.Activities.Validation; +using System.ComponentModel; +using System.Linq.Expressions; +using System.Numerics; + +namespace System.Activities.Expressions +{ + /// + /// A code activity which deccrementally assigns a numeral. + /// + /// + /// int myNum = 5; + /// myNum += 5; // 10. + /// + /// The operand to modify. + /// The operand with which to modify . + /// The resulting type of hte operation. + public sealed class SubtractAssign : CodeActivity +#if NET7_0_OR_GREATER + where TLeft : INumber + where TRight : INumber + where TResult : INumber +#endif + { + private static Func? checkedOperationFunction = null; + private static Func? uncheckedOperationFunction = null; + + /// + /// Gets or sets the left operand which will be modified by this operation. + /// + [RequiredArgument] + public InArgument Left { get; set; } = new(); + + /// + /// Gets or sets the right operand which will be the modifier for this operation. + /// + [RequiredArgument] + public InArgument Right { get; set; } = new(); + + /// + /// Gets or sets a value indicating whether this operation will happen in a checked or unchecked context. + /// + [DefaultValue(true)] + public bool IsChecked { get; set; } = true; + + /// + protected override void CacheMetadata(CodeActivityMetadata metadata) + { + BinaryExpressionHelper.OnGetArguments(metadata, Left, Right); + + if (IsChecked) + { + EnsureOperationFunction(metadata, ref checkedOperationFunction, ExpressionType.SubtractAssignChecked); + } + else + { + EnsureOperationFunction(metadata, ref uncheckedOperationFunction, ExpressionType.SubtractAssign); + } + } + + private static void EnsureOperationFunction + ( + CodeActivityMetadata metadata, + ref Func? operationFunction, + ExpressionType operatorType + ) + { + if (operationFunction == null) + { + if (!BinaryExpressionHelper.TryGenerateLinqDelegate( + operatorType, + out operationFunction, + out ValidationError? validationError)) + { + metadata.AddValidationError(validationError); + } + } + } + + /// + protected override TResult Execute(CodeActivityContext context) + { + TLeft leftValue = Left.Get(context); + TRight rightValue = Right.Get(context); + + // If user changed checked flag between open and execution, an NRE may be thrown. + // This is by design. + // Nullability check silenced because the corresponding value is guaranteed to be + // non-null + return IsChecked + ? checkedOperationFunction!(leftValue, rightValue) + : uncheckedOperationFunction!(leftValue, rightValue); + } + } +} diff --git a/src/UiPath.Workflow.Runtime/Expressions/UnaryExpressionHelper.cs b/src/UiPath.Workflow.Runtime/Expressions/UnaryExpressionHelper.cs index 714f1879..977ce3f7 100644 --- a/src/UiPath.Workflow.Runtime/Expressions/UnaryExpressionHelper.cs +++ b/src/UiPath.Workflow.Runtime/Expressions/UnaryExpressionHelper.cs @@ -8,8 +8,17 @@ namespace System.Activities.Expressions; -internal static class UnaryExpressionHelper +/// +/// Helpers for unary expressions. +/// +public static class UnaryExpressionHelper { + /// + /// Binds the metadata for the argument. + /// + /// The type of the argument. + /// The metadata. + /// The argument. public static void OnGetArguments(CodeActivityMetadata metadata, InArgument operand) { RuntimeArgument operandArgument = new("Operand", typeof(TOperand), ArgumentDirection.In, true); @@ -22,6 +31,33 @@ public static void OnGetArguments(CodeActivityMetadata metadata, InArg }); } + /// + /// Binds the metadata for the argument. + /// + /// The type of the argument. + /// The metadata. + /// The argument. + public static void OnGetArguments(CodeActivityMetadata metadata, InOutArgument operand) + { + RuntimeArgument operandArgument = new("Operand", typeof(TOperand), ArgumentDirection.InOut, true); + metadata.Bind(operand, operandArgument); + + metadata.SetArgumentsCollection( + new Collection + { + operandArgument + }); + } + + /// + /// Generates a delegate. + /// + /// The type of the argument. + /// The return type of the operation. + /// The type of expression. + /// The resulting . + /// If the operation failed, the error. + /// if the operation was successful; otherwise, . public static bool TryGenerateLinqDelegate(ExpressionType operatorType, out Func operation, out ValidationError validationError) { operation = null;