forked from Velocidex/vfilter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexplain.go
243 lines (200 loc) · 5.88 KB
/
explain.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
240
241
242
243
package vfilter
/* This file implements the explain algorithm.
We use reflection to explain all VQL extensions.
*/
import (
"reflect"
"regexp"
"strings"
"github.com/Velocidex/ordereddict"
)
var (
field_regex = regexp.MustCompile("field=([a-zA-Z0-9_]+)")
)
// Populated with information about the scope.
type ScopeInformation struct {
Plugins []*PluginInfo
Functions []*FunctionInfo
}
// Describes the specific plugin.
type PluginInfo struct {
// The name of the plugin.
Name string
// A helpful description about the plugin.
Doc string
ArgType string
// A version of this plugin. VQL queries can target certain
// versions of this plugin if needed.
Version int
}
// Describe functions.
type FunctionInfo struct {
Name string
Doc string
ArgType string
// This is true for functions which operate on aggregates
// (i.e. group by). For any columns which contains such a
// function, vfilter will first run the group by clause then
// re-evaluate the function on the aggregate column.
IsAggregate bool
// A version of this plugin. VQL queries can target certain
// versions of this function if needed.
Version int
}
// Describe a type. This is meant for human consumption so it does not
// need to be so accurate. Fields is a map between the Associative
// member and the type that is supposed to be returned. Note that
// Velocifilter automatically calls accessor methods so they look like
// standard exported fields.
type TypeDescription struct {
Fields *ordereddict.Dict
}
// This describes what type is returned when we reference this field
// from the TypeDescription.
type TypeReference struct {
Target string
Repeated bool
Tag string
}
// Map between type name and its description.
type TypeMap struct {
desc *ordereddict.Dict
}
func NewTypeMap() *TypeMap {
return &TypeMap{
desc: ordereddict.NewDict(),
}
}
func canonicalTypeName(a_type reflect.Type) string {
return strings.TrimLeft(a_type.String(), "*[]")
}
func (self *TypeMap) Get(scope *Scope, name string) (*TypeDescription, bool) {
res, pres := self.desc.Get(name)
if res != nil {
return res.(*TypeDescription), pres
}
return nil, false
}
// Introspect the type of the parameter. Add type descriptor to the
// type map and return the type name.
func (self *TypeMap) AddType(scope *Scope, a Any) string {
// Dont do anything if the caller does not care about a type
// map.
if self == nil || scope == nil {
return ""
}
fields := scope.GetMembers(a)
v := reflect.ValueOf(a)
if v.Type().Kind() == reflect.Ptr {
v = v.Elem()
}
a_type := v.Type()
self.addType(scope, a_type, &fields)
return canonicalTypeName(a_type)
}
func (self *TypeMap) addType(scope *Scope, a_type reflect.Type, fields *[]string) {
_, pres := self.desc.Get(canonicalTypeName(a_type))
if pres {
return
}
result := TypeDescription{
Fields: ordereddict.NewDict(),
}
self.desc.Set(canonicalTypeName(a_type), &result)
self.addFields(scope, a_type, &result, fields)
self.addMethods(scope, a_type, &result, fields)
}
func (self *TypeMap) addFields(scope *Scope, a_type reflect.Type, desc *TypeDescription,
fields *[]string) {
if a_type.Kind() != reflect.Struct {
return
}
for i := 0; i < a_type.NumField(); i++ {
field_value := a_type.Field(i)
// Embedded structs just merge their fields with this
// struct.
if field_value.Anonymous {
self.addFields(scope, field_value.Type, desc, fields)
continue
}
// Skip un-exported names.
if !is_exported(field_value.Name) {
continue
}
// Ignore missing fields.
if len(*fields) > 0 && !InString(fields, field_value.Name) {
continue
}
return_type := field_value.Type
return_type_descriptor := TypeReference{
Target: canonicalTypeName(return_type),
Tag: field_value.Tag.Get("vfilter"),
}
switch return_type.Kind() {
case reflect.Array, reflect.Slice:
element := return_type.Elem()
self.addType(scope, element, fields)
return_type_descriptor.Target = canonicalTypeName(
return_type.Elem())
return_type_descriptor.Repeated = true
case reflect.Map, reflect.Ptr:
element := return_type.Elem()
self.addType(scope, element, fields)
return_type_descriptor.Target = canonicalTypeName(
return_type.Elem())
}
name := field_value.Name
m := field_regex.FindStringSubmatch(return_type_descriptor.Tag)
if len(m) > 1 {
name = m[1]
}
desc.Fields.Set(name, &return_type_descriptor)
}
}
func (self *TypeMap) addMethods(scope *Scope, a_type reflect.Type,
desc *TypeDescription, fields *[]string) {
// If a method has a pointer receiver than we will be able to
// reflect on its literal type. We need to work on pointers.
if a_type.Kind() != reflect.Ptr {
a_type = reflect.PtrTo(a_type)
}
for i := 0; i < a_type.NumMethod(); i++ {
method_value := a_type.Method(i)
// Skip un-exported names.
if !is_exported(method_value.Name) {
continue
}
// Ignore missing fields.
if len(*fields) > 0 && !InString(fields, method_value.Name) {
continue
}
// VFilter only supports calling accessors with no args.
if !method_value.Func.IsValid() ||
method_value.Func.Type().NumIn() != 1 {
continue
}
// VFilter only supports methods returning a single
// value, or possible an error parameter.
switch method_value.Func.Type().NumOut() {
case 1, 2:
return_type := method_value.Func.Type().Out(0)
return_type_descriptor := TypeReference{
Target: canonicalTypeName(return_type),
}
switch return_type.Kind() {
case reflect.Array, reflect.Slice:
element := return_type.Elem()
self.addType(scope, element, fields)
return_type_descriptor.Target = canonicalTypeName(
return_type.Elem())
return_type_descriptor.Repeated = true
case reflect.Map, reflect.Ptr:
element := return_type.Elem()
self.addType(scope, element, fields)
return_type_descriptor.Target = canonicalTypeName(
return_type.Elem())
}
desc.Fields.Set(method_value.Name, &return_type_descriptor)
}
}
}