Skip to content

Commit 247a6a6

Browse files
committed
address PR comments and minor improvements
1 parent 40d2c14 commit 247a6a6

File tree

6 files changed

+93
-57
lines changed

6 files changed

+93
-57
lines changed

checker/nature/nature.go

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -190,23 +190,36 @@ func ArrayFromType(c *Cache, t reflect.Type) Nature {
190190
func (n *Nature) SetCache(c *Cache) {
191191
n.cache = c
192192
if n.Kind == reflect.Struct {
193-
n.structData.cache = c
194193
if c.structs == nil {
194+
n.structData.setCache(c)
195195
c.structs = map[reflect.Type]Nature{
196196
n.Type: *n,
197197
}
198198
} else if nt, ok := c.structs[n.Type]; ok {
199199
// invalidate local, use shared from cache
200200
n.Optional.structData = nt.Optional.structData
201201
} else {
202+
n.structData.setCache(c)
202203
c.structs[n.Type] = *n
203204
}
204205
}
205-
if n.Optional != nil {
206-
if s, ok := c.methods[n.Type]; ok {
207-
// invalidate local if set, use shared from cache
206+
hasMethodset := n.Optional != nil && n.Optional.methodset != nil
207+
if c.methods != nil || hasMethodset {
208+
if c.methods == nil {
209+
// Cache is new and the type already gathered some methods
210+
n.Optional.methodset.setCache(c)
211+
c.methods = map[reflect.Type]*methodset{
212+
n.Type: n.Optional.methodset,
213+
}
214+
} else if s, ok := c.methods[n.Type]; ok {
215+
if n.Optional == nil {
216+
n.Optional = new(Optional)
217+
}
218+
// Cache is not new. Invalidate local if set
208219
n.Optional.methodset = s
209-
} else if n.Optional.methodset != nil {
220+
} else if hasMethodset {
221+
// Cache miss and the type already gathered some methods
222+
n.Optional.methodset.setCache(c)
210223
c.methods[n.Type] = n.Optional.methodset
211224
}
212225
}
@@ -228,7 +241,7 @@ func (n *Nature) String() string {
228241
}
229242

230243
func (n *Nature) Deref() Nature {
231-
t, _, changed := derefTypeKind(n.Type, n.Kind)
244+
t, _, changed := deref.TypeKind(n.Type, n.Kind)
232245
if !changed {
233246
return *n
234247
}
@@ -294,7 +307,7 @@ func (n *Nature) NumMethods() int {
294307

295308
func (n *Nature) MethodByName(name string) (Nature, bool) {
296309
if s := n.getMethodset(); s != nil {
297-
if m, ok := s.method(name); ok {
310+
if m := s.method(name); m != nil {
298311
return m.nature, true
299312
}
300313
}
@@ -378,7 +391,7 @@ func (n *Nature) FieldByName(name string) (Nature, bool) {
378391
} else {
379392
sd = n.cache.getStruct(n.Type).structData
380393
}
381-
if sf, ok := sd.structField(nil, name); ok {
394+
if sf := sd.structField(nil, name); sf != nil {
382395
return sf.Nature, true
383396
}
384397
return Nature{}, false
@@ -419,7 +432,7 @@ func (n *Nature) getSlow(name string) (Nature, bool) {
419432
return nt, true
420433
}
421434
if n.Kind == reflect.Struct {
422-
if sf, ok := n.structField(nil, name); ok {
435+
if sf := n.structField(nil, name); sf != nil {
423436
return sf.Nature, true
424437
}
425438
}
@@ -430,7 +443,7 @@ func (n *Nature) FieldIndex(name string) ([]int, bool) {
430443
if n.Kind != reflect.Struct {
431444
return nil, false
432445
}
433-
if sf, ok := n.structField(nil, name); ok {
446+
if sf := n.structField(nil, name); sf != nil {
434447
return sf.Index, true
435448
}
436449
return nil, false

checker/nature/utils.go

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,9 @@ package nature
22

33
import (
44
"reflect"
5-
)
65

7-
func derefTypeKind(t reflect.Type, k reflect.Kind) (_ reflect.Type, _ reflect.Kind, changed bool) {
8-
for k == reflect.Pointer {
9-
changed = true
10-
t = t.Elem()
11-
k = t.Kind()
12-
}
13-
return t, k, changed
14-
}
6+
"github.com/expr-lang/expr/internal/deref"
7+
)
158

169
func fieldName(fieldName string, tag reflect.StructTag) (string, bool) {
1710
switch taggedName := tag.Get("expr"); taggedName {
@@ -27,7 +20,7 @@ func fieldName(fieldName string, tag reflect.StructTag) (string, bool) {
2720
type structData struct {
2821
cache *Cache
2922
rType reflect.Type
30-
fields map[string]structField
23+
fields map[string]*structField
3124
numField, ownIdx, anonIdx int
3225

3326
curParent, curChild *structData
@@ -39,22 +32,29 @@ type structField struct {
3932
Index []int
4033
}
4134

35+
func (s *structData) setCache(c *Cache) {
36+
s.cache = c
37+
for _, sf := range s.fields {
38+
sf.SetCache(c)
39+
}
40+
}
41+
4242
func (s *structData) finished() bool {
4343
return s.ownIdx >= s.numField && // no own fields left to visit
4444
s.anonIdx >= s.numField && // no embedded fields to visit
4545
s.curChild == nil // no child in process of visiting
4646
}
4747

48-
func (s *structData) structField(parentEmbed *structData, name string) (structField, bool) {
48+
func (s *structData) structField(parentEmbed *structData, name string) *structField {
4949
if s.fields == nil {
5050
if s.numField > 0 {
51-
s.fields = make(map[string]structField, s.numField)
51+
s.fields = make(map[string]*structField, s.numField)
5252
}
53-
} else if f, ok := s.fields[name]; ok {
54-
return f, true
53+
} else if f := s.fields[name]; f != nil {
54+
return f
5555
}
5656
if s.finished() {
57-
return structField{}, false
57+
return nil
5858
}
5959

6060
// Lookup own fields first.
@@ -73,7 +73,7 @@ func (s *structData) structField(parentEmbed *structData, name string) (structFi
7373
continue
7474
}
7575
nt := s.cache.FromType(field.Type)
76-
sf := structField{
76+
sf := &structField{
7777
Nature: nt,
7878
Index: field.Index,
7979
}
@@ -82,14 +82,14 @@ func (s *structData) structField(parentEmbed *structData, name string) (structFi
8282
parentEmbed.trySet(fName, sf)
8383
}
8484
if fName == name {
85-
return sf, true
85+
return sf
8686
}
8787
}
8888

8989
if s.curChild != nil {
90-
sf, ok := s.findInEmbedded(parentEmbed, s.curChild, s.curChildIndex, name)
91-
if ok {
92-
return sf, true
90+
sf := s.findInEmbedded(parentEmbed, s.curChild, s.curChildIndex, name)
91+
if sf != nil {
92+
return sf
9393
}
9494
}
9595

@@ -101,26 +101,26 @@ func (s *structData) structField(parentEmbed *structData, name string) (structFi
101101
if !field.Anonymous {
102102
continue
103103
}
104-
t, k, _ := derefTypeKind(field.Type, field.Type.Kind())
104+
t, k, _ := deref.TypeKind(field.Type, field.Type.Kind())
105105
if k != reflect.Struct {
106106
continue
107107
}
108108

109109
childEmbed := s.cache.getStruct(t).structData
110-
sf, ok := s.findInEmbedded(parentEmbed, childEmbed, field.Index, name)
111-
if ok {
112-
return sf, true
110+
sf := s.findInEmbedded(parentEmbed, childEmbed, field.Index, name)
111+
if sf != nil {
112+
return sf
113113
}
114114
}
115115

116-
return structField{}, false
116+
return nil
117117
}
118118

119119
func (s *structData) findInEmbedded(
120120
parentEmbed, childEmbed *structData,
121121
childIndex []int,
122122
name string,
123-
) (structField, bool) {
123+
) *structField {
124124
// Set current parent/child data. This allows trySet to handle child fields
125125
// and add them to our struct and to the parent as well if needed
126126
s.curParent = parentEmbed
@@ -145,29 +145,29 @@ func (s *structData) findInEmbedded(
145145
}
146146

147147
// Recheck if we have what we needed from the above sync
148-
if sf, ok := s.fields[name]; ok {
149-
return sf, true
148+
if sf := s.fields[name]; sf != nil {
149+
return sf
150150
}
151151

152152
// Try finding in the child again in case it hasn't finished
153153
if !childEmbed.finished() {
154-
if _, ok := childEmbed.structField(s, name); ok {
155-
return s.fields[name], true
154+
if childEmbed.structField(s, name) != nil {
155+
return s.fields[name]
156156
}
157157
}
158158

159-
return structField{}, false
159+
return nil
160160
}
161161

162-
func (s *structData) trySet(name string, sf structField) {
162+
func (s *structData) trySet(name string, sf *structField) {
163163
if _, ok := s.fields[name]; ok {
164164
return
165165
}
166-
sf.Index = append(s.curChildIndex, sf.Index...)
167-
s.fields[name] = structField{
166+
sf = &structField{
168167
Nature: sf.Nature,
169-
Index: sf.Index,
168+
Index: append(s.curChildIndex, sf.Index...),
170169
}
170+
s.fields[name] = sf
171171
if s.curParent != nil {
172172
s.curParent.trySet(name, sf)
173173
}
@@ -178,7 +178,7 @@ func StructFields(c *Cache, t reflect.Type) map[string]Nature {
178178
if t == nil {
179179
return table
180180
}
181-
t, k, _ := derefTypeKind(t, t.Kind())
181+
t, k, _ := deref.TypeKind(t, t.Kind())
182182
if k == reflect.Struct {
183183
// lookup for a field with an empty name, which will cause to never find a
184184
// match, meaning everything will have been cached.
@@ -195,7 +195,7 @@ type methodset struct {
195195
cache *Cache
196196
rType reflect.Type
197197
kind reflect.Kind
198-
methods map[string]method
198+
methods map[string]*method
199199
numMethod, idx int
200200
}
201201

@@ -204,11 +204,18 @@ type method struct {
204204
nature Nature
205205
}
206206

207-
func (s *methodset) method(name string) (method, bool) {
207+
func (s *methodset) setCache(c *Cache) {
208+
s.cache = c
209+
for _, m := range s.methods {
210+
m.nature.SetCache(c)
211+
}
212+
}
213+
214+
func (s *methodset) method(name string) *method {
208215
if s.methods == nil {
209-
s.methods = make(map[string]method, s.numMethod)
210-
} else if m, ok := s.methods[name]; ok {
211-
return m, true
216+
s.methods = make(map[string]*method, s.numMethod)
217+
} else if m := s.methods[name]; m != nil {
218+
return m
212219
}
213220
for ; s.idx < s.numMethod; s.idx++ {
214221
rm := s.rType.Method(s.idx)
@@ -227,14 +234,14 @@ func (s *methodset) method(name string) (method, bool) {
227234
// different indexes for different types which implement
228235
// the same interface.
229236
}
230-
m := method{
237+
m := &method{
231238
Method: rm,
232239
nature: nt,
233240
}
234241
s.methods[rm.Name] = m
235242
if rm.Name == name {
236-
return m, true
243+
return m
237244
}
238245
}
239-
return method{}, false
246+
return nil
240247
}

conf/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func New(env any) *Config {
6363

6464
func (c *Config) WithEnv(env any) {
6565
c.EnvObject = env
66-
c.Env = Env(&c.NtCache, env)
66+
c.Env = EnvWithCache(&c.NtCache, env)
6767
c.Strict = c.Env.Strict
6868
}
6969

conf/env.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ import (
99
"github.com/expr-lang/expr/types"
1010
)
1111

12-
func Env(c *Cache, env any) Nature {
12+
// Env returns the Nature of the given environment.
13+
//
14+
// Deprecated: use EnvWithCache instead.
15+
func Env(env any) Nature {
16+
return EnvWithCache(new(Cache), env)
17+
}
18+
19+
func EnvWithCache(c *Cache, env any) Nature {
1320
if env == nil {
1421
n := c.NatureOf(map[string]any{})
1522
n.Strict = true

docgen/docgen.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func CreateDoc(i any) *Context {
8585
PkgPath: deref.Type(reflect.TypeOf(i)).PkgPath(),
8686
}
8787

88-
env := conf.Env(new(nature.Cache), i)
88+
env := conf.EnvWithCache(new(nature.Cache), i)
8989
for name, t := range env.All() {
9090
if _, ok := c.Variables[Identifier(name)]; ok {
9191
continue

internal/deref/deref.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,12 @@ func Value(v reflect.Value) reflect.Value {
4545
}
4646
return v
4747
}
48+
49+
func TypeKind(t reflect.Type, k reflect.Kind) (_ reflect.Type, _ reflect.Kind, changed bool) {
50+
for k == reflect.Pointer {
51+
changed = true
52+
t = t.Elem()
53+
k = t.Kind()
54+
}
55+
return t, k, changed
56+
}

0 commit comments

Comments
 (0)