-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Add .NET 10 support: fix InvalidProgramException in expression compilation #1429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4d60ab4
56206b8
9d2d787
3299426
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -318,12 +318,14 @@ private void AttachDirectlyOutput(KeyValuePair<string, string> output, WorkflowS | |
| propertyInfo = dataType.GetProperty("Item"); | ||
| targetProperty = Expression.Property(dataParameter, propertyInfo, Expression.Constant(output.Key)); | ||
|
|
||
| var compiledSourceExpr = sourceExpr.Compile(); | ||
|
|
||
| Action<IStepBody, object> acn = (pStep, pData) => | ||
| { | ||
| object resolvedValue; | ||
| try | ||
| { | ||
| resolvedValue = sourceExpr.Compile().DynamicInvoke(pStep); | ||
| resolvedValue = compiledSourceExpr.DynamicInvoke(pStep); | ||
| } | ||
| catch (TargetInvocationException ex) | ||
| { | ||
|
|
@@ -377,13 +379,16 @@ private void AttachNestedOutput(KeyValuePair<string, string> output, WorkflowSte | |
| } | ||
| propertyInfo = ((PropertyInfo)memberExpression.Member).PropertyType.GetProperty("Item"); | ||
|
|
||
| var targetExpr = Expression.Lambda(memberExpression, dataParameter); | ||
| var compiledTargetExpr = targetExpr.Compile(); | ||
| var compiledSourceExpr = sourceExpr.Compile(); | ||
|
|
||
| Action<IStepBody, object> acn = (pStep, pData) => | ||
| { | ||
| var targetExpr = Expression.Lambda(memberExpression, dataParameter); | ||
| object data; | ||
| try | ||
| { | ||
| data = targetExpr.Compile().DynamicInvoke(pData); | ||
| data = compiledTargetExpr.DynamicInvoke(pData); | ||
| } | ||
| catch (TargetInvocationException ex) | ||
| { | ||
|
|
@@ -392,7 +397,7 @@ private void AttachNestedOutput(KeyValuePair<string, string> output, WorkflowSte | |
| object resolvedValue; | ||
| try | ||
| { | ||
| resolvedValue = sourceExpr.Compile().DynamicInvoke(pStep); | ||
| resolvedValue = compiledSourceExpr.DynamicInvoke(pStep); | ||
| } | ||
| catch (TargetInvocationException ex) | ||
| { | ||
|
|
@@ -470,12 +475,14 @@ private static Action<IStepBody, object, IStepExecutionContext> BuildScalarInput | |
| throw new WorkflowDefinitionLoadException($"Error parsing input expression '{expr}' for property '{input.Key}': {ex.Message}", ex); | ||
| } | ||
|
|
||
| var compiledExpr = sourceExpr.Compile(); | ||
|
|
||
| void acn(IStepBody pStep, object pData, IStepExecutionContext pContext) | ||
| { | ||
| object resolvedValue; | ||
| try | ||
| { | ||
| resolvedValue = sourceExpr.Compile().DynamicInvoke(pData, pContext, Environment.GetEnvironmentVariables()); | ||
| resolvedValue = compiledExpr.DynamicInvoke(pData, pContext, Environment.GetEnvironmentVariables()); | ||
| } | ||
| catch (TargetInvocationException ex) | ||
| { | ||
|
|
@@ -505,6 +512,40 @@ void acn(IStepBody pStep, object pData, IStepExecutionContext pContext) | |
|
|
||
| private static Action<IStepBody, object, IStepExecutionContext> BuildObjectInputAction(KeyValuePair<string, object> input, ParameterExpression dataParameter, ParameterExpression contextParameter, ParameterExpression environmentVarsParameter, PropertyInfo stepProperty) | ||
| { | ||
| // Pre-compile all @-prefixed property expressions at definition load time | ||
| var compiledExpressions = new Dictionary<string, Delegate>(); | ||
| var templateObj = JObject.FromObject(input.Value); | ||
| var scanStack = new Stack<JObject>(); | ||
| scanStack.Push(templateObj); | ||
|
|
||
| while (scanStack.Count > 0) | ||
| { | ||
| var subobj = scanStack.Pop(); | ||
| foreach (var prop in subobj.Properties()) | ||
| { | ||
| if (prop.Name.StartsWith("@")) | ||
| { | ||
| var exprText = prop.Value.ToString(); | ||
| if (!compiledExpressions.ContainsKey(exprText)) | ||
| { | ||
| LambdaExpression sourceExpr; | ||
| try | ||
| { | ||
| sourceExpr = DynamicExpressionParser.ParseLambda(ParsingConfig, false, new[] { dataParameter, contextParameter, environmentVarsParameter }, typeof(object), TransformExpression(exprText)); | ||
| } | ||
| catch (Exception ex) when (ex is System.Linq.Dynamic.Core.Exceptions.ParseException || ex is InvalidOperationException) | ||
| { | ||
| throw new WorkflowDefinitionLoadException($"Error parsing input expression '{exprText}': {ex.Message}", ex); | ||
| } | ||
| compiledExpressions[exprText] = sourceExpr.Compile(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| foreach (var child in subobj.Children<JObject>()) | ||
| scanStack.Push(child); | ||
| } | ||
|
|
||
| void acn(IStepBody pStep, object pData, IStepExecutionContext pContext) | ||
| { | ||
| var stack = new Stack<JObject>(); | ||
|
|
@@ -518,11 +559,11 @@ void acn(IStepBody pStep, object pData, IStepExecutionContext pContext) | |
| { | ||
| if (prop.Name.StartsWith("@")) | ||
| { | ||
| var sourceExpr = DynamicExpressionParser.ParseLambda(ParsingConfig, false, new[] { dataParameter, contextParameter, environmentVarsParameter }, typeof(object), TransformExpression(prop.Value.ToString())); | ||
| var exprText = prop.Value.ToString(); | ||
| object resolvedValue; | ||
| try | ||
|
Comment on lines
560
to
564
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This |
||
| { | ||
| resolvedValue = sourceExpr.Compile().DynamicInvoke(pData, pContext, Environment.GetEnvironmentVariables()); | ||
| resolvedValue = compiledExpressions[exprText].DynamicInvoke(pData, pContext, Environment.GetEnvironmentVariables()); | ||
| } | ||
| catch (TargetInvocationException ex) | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| namespace WorkflowCore.TestAssets.DataTypes | ||
| { | ||
| public class ScalarInputData | ||
| { | ||
| public string MessageId { get; set; } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| using WorkflowCore.Interface; | ||
| using WorkflowCore.Models; | ||
|
|
||
| namespace WorkflowCore.TestAssets.Steps | ||
| { | ||
| public class ScalarInputStep : StepBody | ||
| { | ||
| public string MessageId { get; set; } | ||
|
|
||
| public string Status { get; set; } | ||
|
|
||
| public override ExecutionResult Run(IStepExecutionContext context) | ||
| { | ||
| return ExecutionResult.Next(); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, but this is intentional here: the pre-scan deliberately mirrors the exact same
Children<JObject>()traversal the runtimeacnuses below, so the pre-compiledcompiledExpressionsdictionary is guaranteed to contain a key for every@-property the runtime scan will later look up — noKeyNotFoundrisk. The nested-object limitation you spotted is pre-existing behavior onmaster(this PR only lifts the compile step out of the closure to fix the .NET 10InvalidProgramException); making the template walk recurse into nested objects is a separate behavior change I would rather not fold into this fix.