Skip to content

Commit 6a7e526

Browse files
authored
Merge pull request #38 from navtech-io/develop
Add Simpleflow runtime exception information, include line number and statement
2 parents fa64679 + ac3bfec commit 6a7e526

15 files changed

+255
-16
lines changed

src/Simpleflow/CodeGenerator/SimpleflowCodeVisitor.VisitFunction.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,13 @@ private List<Expression> GetArgumentExpressions(SimpleflowParser.FunctionContext
5454
var actualMethodParameters = methodInfo.GetParameters();
5555
var arguments = context.functionArguments().functionArgument();
5656
var argumentsExpressions = new List<Expression>();
57-
57+
5858
if (arguments == null)
5959
{
6060
return argumentsExpressions;
6161
}
6262

63-
CheckInvalidParameters(actualMethodParameters, arguments);
63+
CheckInvalidParameters(actualMethodParameters, arguments, context.FunctionName().GetText());
6464
CheckRepeatedParameters(arguments);
6565

6666
foreach (var methodParameter in actualMethodParameters)
@@ -113,14 +113,16 @@ where g.Count() > 1
113113
}
114114
}
115115

116-
private void CheckInvalidParameters(ParameterInfo[] actualMethodParameters, SimpleflowParser.FunctionArgumentContext[] parameters)
116+
private void CheckInvalidParameters(ParameterInfo[] actualMethodParameters,
117+
SimpleflowParser.FunctionArgumentContext[] parameters,
118+
string functionName)
117119
{
118120
foreach (var parameter in parameters)
119121
{
120122
var paramterName = parameter.Identifier().GetText();
121123
if (!actualMethodParameters.Any(p => string.Equals(p.Name, paramterName, System.StringComparison.OrdinalIgnoreCase)))
122124
{
123-
throw new InvalidFunctionParameterNameException(paramterName);
125+
throw new InvalidFunctionParameterNameException(paramterName, functionName);
124126
}
125127
}
126128
}
@@ -141,12 +143,14 @@ private Expression VisitObjectIdentiferAsPerTargetType(SimpleflowParser.ObjectId
141143

142144
private Expression CreateSmartVariableIfObjectIdentiferNotDefined(Type targetType, string name)
143145
{
144-
// Variable names are not case sensitive
146+
// Variable name is not case sensitive
145147
var smartVar = GetSmartVariable(name);
146148

147149
if (smartVar == null)
148150
{
149-
throw new InvalidFunctionParameterNameException(name);
151+
// Since smart variable (JSON) can only be used with function argument,
152+
// so here we need to throw function related exception
153+
throw new InvalidFunctionParameterNameException($"Invalid parameter or variable '{name}'");
150154
}
151155

152156
// Return if already created

src/Simpleflow/CodeGenerator/SimpleflowCodeVisitor.VisitObjectIdentifier.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ private Expression GetFinalPropertyValue(Expression propExp, SimpleflowParser.Id
7878

7979
if (field == null)
8080
{
81-
throw new InvalidPropertyException($"Invalid property or field '{propName}'");
81+
throw new InvalidPropertyException(propName);
8282
}
8383
}
8484

src/Simpleflow/CodeGenerator/SimpleflowCodeVisitor.VisitRule.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public override Expression VisitRuleStmt(SimpleflowParser.RuleStmtContext contex
2727

2828
if (childResult != null)
2929
{
30+
statements.Add(SetRuntimeState(c));
3031
statements.Add(childResult);
3132
}
3233
}

src/Simpleflow/CodeGenerator/SimpleflowCodeVisitor.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Linq;
66
using System.Collections.Generic;
77
using System.Linq.Expressions;
8+
using Antlr4.Runtime.Tree;
89

910
using Simpleflow.Parser;
1011
using Simpleflow.Exceptions;
@@ -85,6 +86,9 @@ private void ProcessEachStatement(SimpleflowParser.ProgramContext context, List<
8586

8687
if (childResult != null)
8788
{
89+
// Set runtime state for debugging Simpleflow code
90+
statementExpressions.Add( SetRuntimeState(c) );
91+
8892
/* if current rule is variable statement then store the left expression
8993
as variable identifier in variable collection */
9094
if (c.GetType() == typeof(SimpleflowParser.LetStmtContext))
@@ -230,6 +234,7 @@ private void ReplaceVirtualSmartVariablesWithReal(List<Expression> statementExpr
230234
}
231235
}
232236

237+
233238
/* Create basic set of variables to access in script */
234239
private List<Expression> CreateDefaultVariablesAndAssign()
235240
{
@@ -246,5 +251,16 @@ private List<Expression> CreateDefaultVariablesAndAssign()
246251
};
247252
}
248253

254+
private Expression SetRuntimeState(IParseTree node)
255+
{
256+
var codeLineNumber = ((Antlr4.Runtime.CommonToken)((Antlr4.Runtime.ParserRuleContext)node).Start).Line;
257+
258+
var property = typeof(RuntimeContext)
259+
.GetProperty("LineNumber", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
260+
261+
var propertyExpression = Expression.Property(ScriptHelperContextParam, property);
262+
return Expression.Assign(propertyExpression, Expression.Constant(codeLineNumber));
263+
}
264+
249265
}
250266
}

src/Simpleflow/Exceptions/InvalidFunctionParameterNameException.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,20 @@ namespace Simpleflow.Exceptions
99
/// </summary>
1010
public class InvalidFunctionParameterNameException : SimpleflowException
1111
{
12+
13+
/// <summary>
14+
///
15+
/// </summary>
16+
/// <param name="message"></param>
17+
public InvalidFunctionParameterNameException(string message) : base(message) { }
18+
1219
/// <summary>
1320
/// Initializes a new instance of the <see cref="InvalidFunctionException"/> class with
1421
/// a specified variable name.
1522
/// </summary>
16-
/// <param name="message">The message that describes the error.</param>
17-
public InvalidFunctionParameterNameException(string message) : base(message) { }
23+
/// <param name="parameterName">The message that describes the error.</param>
24+
/// <param name="functionName"></param>
25+
public InvalidFunctionParameterNameException(string parameterName, string functionName)
26+
: base($"Function '{functionName}' does not have parameter '{parameterName}'") { }
1827
}
1928
}

src/Simpleflow/Exceptions/InvalidPropertyException.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ public class InvalidPropertyException : SimpleflowException
1212
/// Initializes a new instance of the <see cref="InvalidPropertyException"/> class with
1313
/// a specified variable name.
1414
/// </summary>
15-
/// <param name="message">The message that describes the error.</param>
16-
public InvalidPropertyException(string message): base(message)
15+
/// <param name="propertyName">The message that describes the error.</param>
16+
public InvalidPropertyException(string propertyName): base($"Invalid property or field '{propertyName}'")
1717
{
1818

1919
}

src/Simpleflow/Exceptions/SimpleflowException.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,15 @@ public SimpleflowException(string message): base(message)
2020
{
2121

2222
}
23+
24+
/// <summary>
25+
///
26+
/// </summary>
27+
/// <param name="message"></param>
28+
/// <param name="innerException"></param>
29+
public SimpleflowException(string message, Exception innerException) : base(message, innerException)
30+
{
31+
32+
}
2333
}
2434
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) navtech.io. All rights reserved.
2+
// See License in the project root for license information.
3+
4+
using System;
5+
6+
namespace Simpleflow.Exceptions
7+
{
8+
/// <summary>
9+
///
10+
/// </summary>
11+
public class SimpleflowRuntimeException : SimpleflowException
12+
{
13+
/// <summary>
14+
///
15+
/// </summary>
16+
/// <param name="message"></param>
17+
/// <param name="lineNumber"></param>
18+
/// <param name="statement"></param>
19+
/// <param name="innerException"></param>
20+
public SimpleflowRuntimeException(string message, int lineNumber, string statement, Exception innerException) : base(message, innerException)
21+
{
22+
LineNumber = lineNumber;
23+
Statement = statement;
24+
}
25+
26+
/// <summary>
27+
/// Gets line number where error has occurred
28+
/// </summary>
29+
public int LineNumber { get; }
30+
31+
/// <summary>
32+
/// Gets statement where error has occurred
33+
/// </summary>
34+
public string Statement { get; }
35+
}
36+
}

src/Simpleflow/RuntimeContext.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ internal RuntimeContext(FlowOutput flowOutput, CancellationToken token)
3737
/// Gets cancellation token
3838
/// </summary>
3939
public CancellationToken CancellationToken => _token;
40+
41+
internal int LineNumber { get;set; }
4042
}
4143
}
4244

src/Simpleflow/Services/ExecutionService.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// See License in the project root for license information.
33

44
using System;
5+
using Simpleflow.Exceptions;
56

67
namespace Simpleflow.Services
78
{
@@ -35,10 +36,22 @@ public void Run<TArg>(FlowContext<TArg> context, NextPipelineService<TArg> next)
3536
// Add trace for debugging
3637
context.Trace?.CreateNewTracePoint(nameof(ExecutionService));
3738

38-
var scriptHelperContext = new RuntimeContext(context.Output,
39+
var scriptContext = new RuntimeContext(context.Output,
3940
context.Options?.CancellationToken ?? default);
4041

41-
context.Internals.CompiledScript?.Invoke(context.Argument, context.Output, scriptHelperContext);
42+
try
43+
{
44+
context.Internals.CompiledScript?.Invoke(context.Argument, context.Output, scriptContext);
45+
}
46+
catch(Exception ex)
47+
{
48+
// Get statement where error has occurred
49+
var lines = context.Script?.Split('\n') ?? new string[] { };
50+
var code = lines.Length >= scriptContext.LineNumber && scriptContext.LineNumber > 0 ? lines[scriptContext.LineNumber-1] : context.Script;
51+
52+
// throw
53+
throw new SimpleflowRuntimeException(ex.Message, scriptContext.LineNumber, code, ex);
54+
}
4255

4356
next?.Invoke(context);
4457
}

0 commit comments

Comments
 (0)