Skip to content

Commit

Permalink
added more safety around panic cases when using parameter accessors
Browse files Browse the repository at this point in the history
  • Loading branch information
Knetic committed Jun 20, 2017
1 parent b40ed7e commit 3518712
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 1 deletion.
24 changes: 24 additions & 0 deletions evaluationFailure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ const (
ABSENT_PARAMETER = "No parameter"
INVALID_REGEX = "Unable to compile regexp pattern"
INVALID_PARAMETER_CALL = "No method or field"
TOO_FEW_ARGS = "reflect: Call with too few input arguments"
TOO_MANY_ARGS = "reflect: Call with too many input arguments"
MISMATCHED_PARAMETERS = "reflect: Call using"
)

// preset parameter map of types that can be used in an evaluation failure test to check typing.
Expand Down Expand Up @@ -468,6 +471,27 @@ func TestInvalidParameterCalls(test *testing.T) {
Parameters: fooFailureParameters,
Expected: "function should always fail",
},
EvaluationFailureTest{

Name: "Too few arguments to parameter call",
Input: "foo.FuncArgStr()",
Parameters: fooFailureParameters,
Expected: TOO_FEW_ARGS,
},
EvaluationFailureTest{

Name: "Too many arguments to parameter call",
Input: "foo.FuncArgStr('foo', 'bar', 15)",
Parameters: fooFailureParameters,
Expected: TOO_MANY_ARGS,
},
EvaluationFailureTest{

Name: "Mismatched parameters",
Input: "foo.FuncArgStr(5)",
Parameters: fooFailureParameters,
Expected: MISMATCHED_PARAMETERS,
},
}

runEvaluationFailureTests(evaluationTests, test)
Expand Down
16 changes: 15 additions & 1 deletion evaluationStage.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"math"
"regexp"
"reflect"
"strings"
)

const (
Expand Down Expand Up @@ -248,7 +249,9 @@ func makeFunctionStage(function ExpressionFunction) evaluationOperator {

func makeAccessorStage(pair []string) evaluationOperator {

return func(left interface{}, right interface{}, parameters Parameters) (interface{}, error) {
reconstructed := strings.Join(pair, ".")

return func(left interface{}, right interface{}, parameters Parameters) (ret interface{}, err error) {

var params []reflect.Value

Expand All @@ -257,6 +260,17 @@ func makeAccessorStage(pair []string) evaluationOperator {
return nil, err
}

// while this library generally tries to handle panic-inducing cases on its own,
// accessors are a sticky case which have a lot of possible ways to fail.
// therefore every call to an accessor sets up a defer that tries to recover from panics, converting them to errors.
defer func() {
if r := recover(); r != nil {
errorMsg := fmt.Sprintf("Failed to access '%s': %v", reconstructed, r.(string))
err = errors.New(errorMsg)
ret = nil
}
}()

for i := 1; i < len(pair); i++ {

coreValue := reflect.ValueOf(value)
Expand Down

0 comments on commit 3518712

Please sign in to comment.