-
Notifications
You must be signed in to change notification settings - Fork 8
/
visit.go
239 lines (229 loc) 路 7.16 KB
/
visit.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
package walkers
import (
"context"
"go/types"
"regexp"
"sync"
"github.com/1pkg/gopium/collections"
"github.com/1pkg/gopium/gopium"
)
// applied encapsulates visited by strategy
// structs results: id, loc, origin, result structs and error
type applied struct {
O gopium.Struct `gopium:"filter_pads,memory_pack,cache_rounding_cpu_l1_discrete,struct_annotate_comment,add_tag_group_force"`
R gopium.Struct `gopium:"filter_pads,memory_pack,cache_rounding_cpu_l1_discrete,struct_annotate_comment,add_tag_group_force"`
ID string `gopium:"filter_pads,memory_pack,cache_rounding_cpu_l1_discrete,struct_annotate_comment,add_tag_group_force"`
Loc string `gopium:"filter_pads,memory_pack,cache_rounding_cpu_l1_discrete,struct_annotate_comment,add_tag_group_force"`
Err error `gopium:"filter_pads,memory_pack,cache_rounding_cpu_l1_discrete,struct_annotate_comment,add_tag_group_force"`
_ [32]byte `gopium:"filter_pads,memory_pack,cache_rounding_cpu_l1_discrete,struct_annotate_comment,add_tag_group_force"`
} // struct size: 256 bytes; struct align: 8 bytes; struct aligned size: 256 bytes; - 馃尯 gopium @1pkg
// appliedCh defines abstraction that helps
// keep applied stream results
type appliedCh chan applied
// govisit defines abstraction that helps
// visit filtered structures on the scope
type govisit func(context.Context, *types.Scope)
// prepare defines abstraction that helps
// setup visiting maven for future visit action
type prepare func() (*maven, context.CancelFunc)
// with helps to create prepare func
// with exposer, locator and backref
func with(exp gopium.Exposer, loc gopium.Locator, bref bool) prepare {
return func() (*maven, context.CancelFunc) {
// create visiting maven with reference
// and return it back,
// with ref prune cancelation func
ref := collections.NewReference(bref)
return &maven{exp: exp, loc: loc, ref: ref}, ref.Prune
}
}
// visit helps to implement Walker Visit method
// depends on deep flag (different tree levels),
// it creates govisit func instance that
// goes through all struct decls inside the scope,
// converts them to inner gopium format
// and applies the strategy if struct name matches regex,
// then it push result of the strategy to the provided chan
func (p prepare) visit(regex *regexp.Regexp, stg gopium.Strategy, ch appliedCh, deep bool) govisit {
// return govisit func applied
// visiting implementation
// that goes through all structures
// with names that match regex
// and applies strategy to them
return func(ctx context.Context, s *types.Scope) {
// prepare visiting maven
m, cancel := p()
defer cancel()
// determinate which function
// should be applied for visiting
// depends on deep flag
if deep {
vdeep(ctx, s, regex, stg, m, ch)
} else {
vscope(ctx, s, regex, stg, m, ch)
}
}
}
// vdeep defines deep visiting helper
// that goes through all structures on all scopes concurently,
// if their names match regex then applies strategy to them
// uses vscope helper to visit single scope
func vdeep(ctx context.Context, s *types.Scope, r *regexp.Regexp, stg gopium.Strategy, m *maven, ch appliedCh) {
// wait until all visits finished
// and then close the channel
defer close(ch)
// wait group visits counter
var wg sync.WaitGroup
// indeep defines recursive inner
// visitig helper that visits
// all scope one by one
// and runs vscope on them
var indeep govisit
indeep = func(ctx context.Context, s *types.Scope) {
// decrement wait group visits counter
// after scope visiting is done
defer wg.Done()
// manage context actions
// in case of cancelation
// break from further traverse
select {
case <-ctx.Done():
// push error to the chan
ch <- applied{Err: ctx.Err()}
return
default:
}
// synchronously visit current scope
// setup chan for current scope
nch := make(appliedCh)
// run vscope on current scope
go vscope(ctx, s, r, stg, m, nch)
// redirect results of vscope
// to final applied chanel
for applied := range nch {
ch <- applied
}
// traverse through children scopes
nc := s.NumChildren()
for i := 0; i < nc; i++ {
// manage context actions
// in case of cancelation
// break from further traverse
select {
case <-ctx.Done():
// push error to the chan
ch <- applied{Err: ctx.Err()}
return
default:
}
// increment wait group
// visits counter for
// each child visiting
wg.Add(1)
// visit children scopes iteratively
// using child context and scope
go indeep(ctx, s.Child(i))
}
}
// increment wait group
// visits counter for
// root visiting
wg.Add(1)
// start indeep chain
go indeep(ctx, s)
// sync all visiting to finish
// the same time
wg.Wait()
}
// vscope defines visiting helper
// that goes through structures on the single scope concurently,
// if their names match regex then applies strategy to them
func vscope(ctx context.Context, s *types.Scope, r *regexp.Regexp, stg gopium.Strategy, m *maven, ch appliedCh) {
// wait until all visits finished
// and then close the channel
defer close(ch)
// wait group visits counter
var wg sync.WaitGroup
// go through all names inside the package scope
// and collect all visiting closure
//
// we can't call them directly in order to backref
// work correctly, as we need in first iteration to
// enumerate all structs on the scope first
names := s.Names()
vclos := make([]func(), 0, len(names))
for _, name := range names {
// manage context actions
// in case of cancelation
// break from further traverse
select {
case <-ctx.Done():
// push error to the chan
ch <- applied{Err: ctx.Err()}
return
default:
}
// check if object name doesn't matches regex
if !r.MatchString(name) {
continue
}
// in case it does and object is
// a type name and it's not an alias for struct
// then apply strategy to it
if tn, ok := s.Lookup(name).(*types.TypeName); ok && !tn.IsAlias() {
// if underlying type is struct
if st, ok := tn.Type().Underlying().(*types.Struct); ok {
// structure's name, id and loc
var name, id, loc string = name, "", ""
// in case id of structure
// has been already visited
if id, loc, ok = m.has(tn); ok {
continue
}
// create struct ref notifier
notif := m.refst(id)
// collect the structure's visiting
// closure that applies strategy to it
vclos = append(vclos, func() {
// decrement visits wait group
defer wg.Done()
// convert original struct
// to inner gopium format
o := m.enum(name, st)
// apply provided strategy
r, err := stg.Apply(ctx, o)
// notify ref with result structure
notif(r)
// and push results to the chan
ch <- applied{
ID: id,
Loc: loc,
O: o,
R: r,
Err: err,
}
})
}
}
}
for _, vclos := range vclos {
// manage context actions
// in case of cancelation
// stop the execution
// it closes applied channel
select {
case <-ctx.Done():
// push error to the chan
ch <- applied{Err: ctx.Err()}
return
default:
}
// increment visits wait group
// and run collected visits
// closures concurently
wg.Add(1)
go vclos()
}
// wait until all visits are finished
wg.Wait()
}