Prevent stack overflow when evaluating input arguments
This commit is contained in:
parent
67efc51ebb
commit
b02755a378
|
|
@ -33,6 +33,13 @@ public class JavaScriptFeature : FeatureBase
|
|||
/// <inheritdoc />
|
||||
public override void Configure()
|
||||
{
|
||||
Module.UseWorkflowManagement(management => management.AddActivitiesFrom<JavaScriptFeature>());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Apply()
|
||||
{
|
||||
// JavaScript services.
|
||||
Services
|
||||
.AddSingleton<IExpressionSyntaxProvider, JavaScriptExpressionSyntaxProvider>()
|
||||
.AddSingleton<IJavaScriptEvaluator, JintJavaScriptEvaluator>()
|
||||
|
|
@ -41,6 +48,7 @@ public class JavaScriptFeature : FeatureBase
|
|||
.AddExpressionHandler<JavaScriptExpressionHandler, JavaScriptExpression>()
|
||||
;
|
||||
|
||||
// Type definition services.
|
||||
Services
|
||||
.AddSingleton<ITypeDefinitionService, TypeDefinitionService>()
|
||||
.AddSingleton<ITypeDescriber, TypeDescriber>()
|
||||
|
|
@ -50,7 +58,8 @@ public class JavaScriptFeature : FeatureBase
|
|||
.AddSingleton<ITypeDefinitionProvider, CommonTypeDefinitionProvider>()
|
||||
.AddSingleton<ITypeDefinitionProvider, VariableTypeDefinitionProvider>()
|
||||
;
|
||||
|
||||
Module.UseWorkflowManagement(management => management.AddActivitiesFrom<JavaScriptFeature>());
|
||||
|
||||
// Handlers.
|
||||
Services.AddNotificationHandlersFrom<JavaScriptFeature>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
using Elsa.Expressions.Models;
|
||||
using Elsa.Expressions.Services;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.JavaScript.Notifications;
|
||||
using Elsa.Mediator.Services;
|
||||
using Elsa.Workflows.Core.Models;
|
||||
using Elsa.Workflows.Management.Activities;
|
||||
using Elsa.Workflows.Management.Extensions;
|
||||
using Elsa.Workflows.Management.Services;
|
||||
using Humanizer;
|
||||
using JetBrains.Annotations;
|
||||
using Jint;
|
||||
|
||||
namespace Elsa.JavaScript.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Configures the JavaScript engine with workflow input getters.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public class WorkflowDefinitionActivityJavaScriptHandler : INotificationHandler<EvaluatingJavaScript>
|
||||
{
|
||||
private readonly IActivityRegistry _activityRegistry;
|
||||
private readonly IExpressionEvaluator _expressionEvaluator;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public WorkflowDefinitionActivityJavaScriptHandler(IActivityRegistry activityRegistry, IExpressionEvaluator expressionEvaluator)
|
||||
{
|
||||
_activityRegistry = activityRegistry;
|
||||
_expressionEvaluator = expressionEvaluator;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task HandleAsync(EvaluatingJavaScript notification, CancellationToken cancellationToken)
|
||||
{
|
||||
var engine = notification.Engine;
|
||||
var context = notification.Context;
|
||||
|
||||
// If we are already evaluating inputs, then we're in a circular evaluation loop. In this case, we should not attempt to evaluate the inputs.
|
||||
if(context.TransientProperties.TryGetValue("EvaluatingInputs", out var evaluatingInputs) && (bool)evaluatingInputs)
|
||||
return;
|
||||
|
||||
// To prevent a circular evaluation loop, set a flag on the context to indicate that we're currently evaluating the inputs.
|
||||
context.TransientProperties["EvaluatingInputs"] = true;
|
||||
|
||||
// Create input getters.
|
||||
await CreateInputAccessorsAsync(engine, context);
|
||||
}
|
||||
|
||||
private async Task CreateInputAccessorsAsync(Engine engine, ExpressionExecutionContext context)
|
||||
{
|
||||
var workflowDefinitionActivity = GetFirstWorkflowDefinitionActivity(context);
|
||||
|
||||
if (workflowDefinitionActivity == null)
|
||||
return;
|
||||
|
||||
var descriptor = _activityRegistry.Find(workflowDefinitionActivity.Type, workflowDefinitionActivity.Version)!;
|
||||
var inputDefinitions = descriptor.Inputs;
|
||||
|
||||
foreach (var inputDefinition in inputDefinitions)
|
||||
{
|
||||
var inputPascalName = inputDefinition.Name.Pascalize();
|
||||
var input = workflowDefinitionActivity.SyntheticProperties.TryGetValue(inputDefinition.Name, out var inputValue) ? (Input?)inputValue : default;
|
||||
var evaluatedExpression = input != null ? await _expressionEvaluator.EvaluateAsync(input, context) : input;
|
||||
|
||||
engine.SetValue($"get{inputPascalName}", (Func<object?>)(() => evaluatedExpression));
|
||||
}
|
||||
}
|
||||
|
||||
private static WorkflowDefinitionActivity? GetFirstWorkflowDefinitionActivity(ExpressionExecutionContext context) =>
|
||||
context.GetActivityExecutionContext().GetFirstWorkflowDefinitionActivity();
|
||||
}
|
||||
|
|
@ -74,8 +74,6 @@ public class JintJavaScriptEvaluator : IJavaScriptEvaluator
|
|||
// Create variable & input setters and getters for each variable.
|
||||
CreateMemoryBlockAccessors(engine, context);
|
||||
|
||||
// Create input getters.
|
||||
await CreateInputAccessorsAsync(engine, context);
|
||||
|
||||
engine.SetValue("isNullOrWhiteSpace", (Func<string, bool>)(value => string.IsNullOrWhiteSpace(value)));
|
||||
engine.SetValue("isNullOrEmpty", (Func<string, bool>)(value => string.IsNullOrEmpty(value)));
|
||||
|
|
@ -93,29 +91,6 @@ public class JintJavaScriptEvaluator : IJavaScriptEvaluator
|
|||
|
||||
return engine;
|
||||
}
|
||||
|
||||
private async Task CreateInputAccessorsAsync(Engine engine, ExpressionExecutionContext context)
|
||||
{
|
||||
var workflowDefinitionActivity = GetFirstWorkflowDefinitionActivity(context);
|
||||
|
||||
if (workflowDefinitionActivity == null)
|
||||
return;
|
||||
|
||||
var descriptor = _activityRegistry.Find(workflowDefinitionActivity.Type, workflowDefinitionActivity.Version)!;
|
||||
var inputDefinitions = descriptor.Inputs;
|
||||
|
||||
foreach (var inputDefinition in inputDefinitions)
|
||||
{
|
||||
var inputPascalName = inputDefinition.Name.Pascalize();
|
||||
var input = workflowDefinitionActivity.SyntheticProperties.TryGetValue(inputDefinition.Name, out var inputValue) ? (Input?)inputValue : default;
|
||||
var evaluatedExpression = input != null ? await _expressionEvaluator.EvaluateAsync(input, context) : input;
|
||||
|
||||
engine.SetValue($"get{inputPascalName}", (Func<object?>)(() => evaluatedExpression));
|
||||
}
|
||||
}
|
||||
|
||||
private static WorkflowDefinitionActivity? GetFirstWorkflowDefinitionActivity(ExpressionExecutionContext context) =>
|
||||
context.GetActivityExecutionContext().GetFirstWorkflowDefinitionActivity();
|
||||
|
||||
private static void CreateMemoryBlockAccessors(Engine engine, ExpressionExecutionContext context)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue