Skip to content

Commit c842159

Browse files
committed
Limit use of reflect nil checking
1 parent 0af2f80 commit c842159

File tree

22 files changed

+55
-75
lines changed

22 files changed

+55
-75
lines changed

excellent/functions/builtin.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1809,7 +1809,7 @@ func JSON(env envs.Environment, value types.XValue) types.XValue {
18091809
//
18101810
// @function format(value)
18111811
func Format(env envs.Environment, value types.XValue) types.XValue {
1812-
if !utils.IsNil(value) {
1812+
if !types.IsNil(value) {
18131813
return types.NewXText(value.Format(env))
18141814
}
18151815
return types.XTextEmpty
@@ -2090,7 +2090,7 @@ func IsError(env envs.Environment, value types.XValue) types.XValue {
20902090
// @function count(value)
20912091
func Count(env envs.Environment, value types.XValue) types.XValue {
20922092
// a nil has count of zero
2093-
if utils.IsNil(value) {
2093+
if types.IsNil(value) {
20942094
return types.XNumberZero
20952095
}
20962096

excellent/tools/context_walk.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package tools
22

33
import (
44
"github.com/nyaruka/goflow/excellent/types"
5-
"github.com/nyaruka/goflow/utils"
65
)
76

87
// ContextWalk traverses the given context invoking the callback for each non-nil value
@@ -21,7 +20,7 @@ func ContextWalkObjects(context *types.XObject, callback func(*types.XObject)) {
2120
}
2221

2322
func contextWalk(v types.XValue, callback func(types.XValue)) {
24-
if utils.IsNil(v) {
23+
if types.IsNil(v) {
2524
return
2625
}
2726

excellent/tree.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"github.com/nyaruka/goflow/excellent/functions"
1010
"github.com/nyaruka/goflow/excellent/operators"
1111
"github.com/nyaruka/goflow/excellent/types"
12-
"github.com/nyaruka/goflow/utils"
1312
)
1413

1514
type Warnings struct {
@@ -43,7 +42,7 @@ func (x *ContextReference) Evaluate(env envs.Environment, scope *Scope, warnings
4342
return types.NewXErrorf("context has no property '%s'", x.name)
4443
}
4544

46-
if !utils.IsNil(value) && value.Deprecated() != "" {
45+
if !types.IsNil(value) && value.Deprecated() != "" {
4746
warnings.deprecatedContext(value)
4847
}
4948

@@ -422,7 +421,7 @@ func resolveLookup(env envs.Environment, container types.XValue, lookup types.XV
422421
return types.NewXErrorf("%s doesn't support lookups", types.Describe(container))
423422
}
424423

425-
if !utils.IsNil(resolved) && resolved.Deprecated() != "" {
424+
if !types.IsNil(resolved) && resolved.Deprecated() != "" {
426425
warnings.deprecatedContext(resolved)
427426
}
428427

excellent/types/array.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ var XArrayEmpty = NewXArray()
139139

140140
// ToXArray converts the given value to an array
141141
func ToXArray(env envs.Environment, x XValue) (*XArray, *XError) {
142-
if utils.IsNil(x) {
142+
if IsNil(x) {
143143
return XArrayEmpty, nil
144144
}
145145
if IsXError(x) {

excellent/types/base.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"reflect"
77

88
"github.com/nyaruka/goflow/envs"
9-
"github.com/nyaruka/goflow/utils"
109
)
1110

1211
// XValue is the base interface of all excellent types
@@ -54,9 +53,9 @@ type XComparable interface {
5453
// specifically means text(x) == text(y)
5554
func Equals(x1 XValue, x2 XValue) bool {
5655
// nil == nil
57-
if utils.IsNil(x1) && utils.IsNil(x2) {
56+
if IsNil(x1) && IsNil(x2) {
5857
return true
59-
} else if utils.IsNil(x1) || utils.IsNil(x2) {
58+
} else if IsNil(x1) || IsNil(x2) {
6059
return false
6160
}
6261

@@ -71,11 +70,11 @@ func Equals(x1 XValue, x2 XValue) bool {
7170
// Compare compares two given values
7271
func Compare(x1 XValue, x2 XValue) int {
7372
// nil == nil
74-
if utils.IsNil(x1) && utils.IsNil(x2) {
73+
if IsNil(x1) && IsNil(x2) {
7574
return 0
76-
} else if utils.IsNil(x1) {
75+
} else if IsNil(x1) {
7776
return -1
78-
} else if utils.IsNil(x2) {
77+
} else if IsNil(x2) {
7978
return 1
8079
}
8180

@@ -99,44 +98,50 @@ func SameType(x1 XValue, x2 XValue) bool {
9998

10099
// Describe returns a representation of the given value for use in error messages
101100
func Describe(x XValue) string {
102-
if utils.IsNil(x) {
101+
if IsNil(x) {
103102
return "null"
104103
}
105104
return x.Describe()
106105
}
107106

108107
// Truthy determines truthiness for the given value
109108
func Truthy(x XValue) bool {
110-
if utils.IsNil(x) {
109+
if IsNil(x) {
111110
return false
112111
}
113112
return x.Truthy()
114113
}
115114

116115
// Render returns the canonical text representation
117116
func Render(x XValue) string {
118-
if utils.IsNil(x) {
117+
if IsNil(x) {
119118
return ""
120119
}
121120
return x.Render()
122121
}
123122

124123
// Format returns the pretty text representation
125124
func Format(env envs.Environment, x XValue) string {
126-
if utils.IsNil(x) {
125+
if IsNil(x) {
127126
return ""
128127
}
129128
return x.Format(env)
130129
}
131130

132131
// String returns a representation of the given value for use in debugging
133132
func String(x XValue) string {
134-
if utils.IsNil(x) {
133+
if IsNil(x) {
135134
return "nil"
136135
}
137136
return x.String()
138137
}
139138

139+
// IsNil returns whether the given value is nil... because of golang's love of autoboxing nil pointers into non-nil
140+
// interfaces, we need to check if interface itself is nil or the underlying pointer is nil.
141+
func IsNil(x XValue) bool {
142+
return x == nil || reflect.ValueOf(x).IsNil()
143+
}
144+
140145
// baseValue is shared by all X types
141146
type baseValue struct {
142147
deprecated string

excellent/types/boolean.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55

66
"github.com/nyaruka/gocommon/jsonx"
77
"github.com/nyaruka/goflow/envs"
8-
"github.com/nyaruka/goflow/utils"
98
)
109

1110
// XBoolean is a boolean `true` or `false`.
@@ -90,7 +89,7 @@ var _ XValue = XBooleanFalse
9089

9190
// ToXBoolean converts the given value to a boolean
9291
func ToXBoolean(x XValue) (*XBoolean, *XError) {
93-
if utils.IsNil(x) {
92+
if IsNil(x) {
9493
return XBooleanFalse, nil
9594
}
9695
if IsXError(x) {

excellent/types/date.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"github.com/nyaruka/gocommon/dates"
77
"github.com/nyaruka/gocommon/jsonx"
88
"github.com/nyaruka/goflow/envs"
9-
"github.com/nyaruka/goflow/utils"
109
)
1110

1211
// XDate is a Gregorian calendar date value.
@@ -82,7 +81,7 @@ var _ XValue = XDateZero
8281

8382
// ToXDate converts the given value to a time or returns an error if that isn't possible
8483
func ToXDate(env envs.Environment, x XValue) (*XDate, *XError) {
85-
if !utils.IsNil(x) {
84+
if !IsNil(x) {
8685
switch typed := x.(type) {
8786
case *XError:
8887
return XDateZero, typed

excellent/types/datetime.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"github.com/nyaruka/gocommon/dates"
88
"github.com/nyaruka/gocommon/jsonx"
99
"github.com/nyaruka/goflow/envs"
10-
"github.com/nyaruka/goflow/utils"
1110
)
1211

1312
// XDateTime is a datetime value.
@@ -135,7 +134,7 @@ func ToXDateTimeWithTimeFill(env envs.Environment, x XValue) (*XDateTime, *XErro
135134

136135
// converts the given value to a time or returns an error if that isn't possible
137136
func toXDateTime(env envs.Environment, x XValue, fillTime bool) (*XDateTime, *XError) {
138-
if !utils.IsNil(x) {
137+
if !IsNil(x) {
139138
switch typed := x.(type) {
140139
case *XError:
141140
return XDateTimeZero, typed

excellent/types/json.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66

77
"github.com/buger/jsonparser"
88
"github.com/nyaruka/gocommon/jsonx"
9-
"github.com/nyaruka/goflow/utils"
109
"github.com/pkg/errors"
1110
"github.com/shopspring/decimal"
1211
)
@@ -82,7 +81,7 @@ func jsonToArray(data []byte) *XArray {
8281

8382
// ToXJSON converts the given value to a JSON string
8483
func ToXJSON(x XValue) (*XText, *XError) {
85-
if utils.IsNil(x) {
84+
if IsNil(x) {
8685
return NewXText(`null`), nil
8786
}
8887
if IsXError(x) {

excellent/types/number.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77

88
"github.com/nyaruka/gocommon/jsonx"
99
"github.com/nyaruka/goflow/envs"
10-
"github.com/nyaruka/goflow/utils"
1110
"github.com/pkg/errors"
1211
"github.com/shopspring/decimal"
1312
)
@@ -152,7 +151,7 @@ func newXNumberFromString(s string) (*XNumber, error) {
152151

153152
// ToXNumber converts the given value to a number or returns an error if that isn't possible
154153
func ToXNumber(env envs.Environment, x XValue) (*XNumber, *XError) {
155-
if !utils.IsNil(x) {
154+
if !IsNil(x) {
156155
switch typed := x.(type) {
157156
case *XError:
158157
return XNumberZero, typed

excellent/types/object.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ var _ json.Marshaler = (*XObject)(nil)
237237

238238
// ToXObject converts the given value to an object
239239
func ToXObject(env envs.Environment, x XValue) (*XObject, *XError) {
240-
if utils.IsNil(x) {
240+
if IsNil(x) {
241241
return XObjectEmpty, nil
242242
}
243243
if IsXError(x) {

excellent/types/text.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77

88
"github.com/nyaruka/gocommon/jsonx"
99
"github.com/nyaruka/goflow/envs"
10-
"github.com/nyaruka/goflow/utils"
1110
)
1211

1312
// XText is a string of characters.
@@ -89,7 +88,7 @@ var _ XValue = XTextEmpty
8988

9089
// ToXText converts the given value to a string
9190
func ToXText(env envs.Environment, x XValue) (*XText, *XError) {
92-
if utils.IsNil(x) {
91+
if IsNil(x) {
9392
return XTextEmpty, nil
9493
}
9594
if IsXError(x) {

excellent/types/time.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"github.com/nyaruka/gocommon/dates"
77
"github.com/nyaruka/gocommon/jsonx"
88
"github.com/nyaruka/goflow/envs"
9-
"github.com/nyaruka/goflow/utils"
109
)
1110

1211
// XTime is a time of day.
@@ -88,7 +87,7 @@ var _ XValue = XTimeZero
8887

8988
// ToXTime converts the given value to a time or returns an error if that isn't possible
9089
func ToXTime(env envs.Environment, x XValue) (*XTime, *XError) {
91-
if !utils.IsNil(x) {
90+
if !IsNil(x) {
9291
switch typed := x.(type) {
9392
case *XError:
9493
return XTimeZero, typed

flows/definition/legacy/expressions/migrate_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import (
1717
"github.com/nyaruka/goflow/excellent/types"
1818
"github.com/nyaruka/goflow/flows/definition/legacy/expressions"
1919
"github.com/nyaruka/goflow/test"
20-
"github.com/nyaruka/goflow/utils"
2120

2221
"github.com/stretchr/testify/assert"
2322
"github.com/stretchr/testify/require"
@@ -277,7 +276,7 @@ func (v legacyVariables) Context(env envs.Environment) *types.XObject {
277276
}
278277

279278
func toXType(env envs.Environment, val any) types.XValue {
280-
if utils.IsNil(val) {
279+
if val == nil {
281280
return nil
282281
}
283282

flows/expressions.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package flows
22

33
import (
4+
"reflect"
45
"strconv"
56

67
"github.com/nyaruka/goflow/envs"
78
"github.com/nyaruka/goflow/excellent/types"
8-
"github.com/nyaruka/goflow/utils"
99
)
1010

1111
// Contextable is an object that can accessed in expressions as a object with properties
@@ -15,12 +15,15 @@ type Contextable interface {
1515

1616
// Context generates a lazy object for use in expressions
1717
func Context(env envs.Environment, contextable Contextable) types.XValue {
18-
if !utils.IsNil(contextable) {
19-
return types.NewXLazyObject(func() map[string]types.XValue {
20-
return contextable.Context(env)
21-
})
18+
// we allow passing nil pointers which will become non-nil Contextables
19+
if contextable == nil || reflect.ValueOf(contextable).IsNil() {
20+
return nil
2221
}
23-
return nil
22+
23+
return types.NewXLazyObject(func() map[string]types.XValue {
24+
return contextable.Context(env)
25+
})
26+
2427
}
2528

2629
// ContextFunc generates a lazy object for use in expressions

flows/field.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"github.com/nyaruka/goflow/assets"
99
"github.com/nyaruka/goflow/envs"
1010
"github.com/nyaruka/goflow/excellent/types"
11-
"github.com/nyaruka/goflow/utils"
1211
)
1312

1413
// Field represents a contact field
@@ -284,7 +283,7 @@ func (f FieldValues) Context(env envs.Environment) map[string]types.XValue {
284283
val := v.ToXValue(env)
285284
entries[string(k)] = val
286285

287-
if !utils.IsNil(val) {
286+
if val != nil {
288287
lines = append(lines, fmt.Sprintf("%s: %s", v.field.Name(), types.Render(val)))
289288
}
290289
}

flows/routers/base.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func (r *baseRouter) Categories() []flows.Category { return r.categories }
5353

5454
// AllowTimeout returns whether this router can be resumed at with a timeout
5555
func (r *baseRouter) AllowTimeout() bool {
56-
return r.wait != nil && !utils.IsNil(r.wait.Timeout())
56+
return r.wait != nil && r.wait.Timeout() != nil
5757
}
5858

5959
// ResultName returns the name which the result of this router should be saved as (if any)

flows/routers/waits/base.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ func newBaseWait(typeName string, timeout *Timeout) baseWait {
4848
func (w *baseWait) Type() string { return w.type_ }
4949

5050
// Timeout returns the timeout of this wait or nil if no timeout is set
51-
func (w *baseWait) Timeout() flows.Timeout { return w.timeout }
51+
func (w *baseWait) Timeout() flows.Timeout {
52+
if w.timeout == nil {
53+
return nil
54+
}
55+
return w.timeout
56+
}
5257

5358
func (w *baseWait) expiresOn(run flows.Run) *time.Time {
5459
expiresAfterMins := run.Flow().ExpireAfterMinutes()

flows/runs/run.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,13 @@ func (r *run) RootContext(env envs.Environment) map[string]types.XValue {
216216
}
217217
}
218218

219-
var child = newRelatedRunContext(r.Session().GetCurrentChild(r))
220-
var parent = newRelatedRunContext(r.Parent())
219+
var child, parent *relatedRunContext
220+
if r.Session().GetCurrentChild(r) != nil {
221+
child = newRelatedRunContext(r.Session().GetCurrentChild(r))
222+
}
223+
if r.Parent() != nil {
224+
parent = newRelatedRunContext(r.Parent())
225+
}
221226

222227
_, n, _ := r.PathLocation()
223228
if n != nil {

0 commit comments

Comments
 (0)