github.com/jlmucb/cloudproxy@v0.0.0-20170830161738-b5aa0b619bc4/go/apps/genauth/visitors.go (about) 1 package genauth 2 3 import ( 4 "go/ast" 5 "go/parser" 6 "go/token" 7 "log" 8 "strings" 9 ) 10 11 var primitives = map[string]bool{ 12 "bool": true, 13 "int": true, 14 "int64": true, 15 "string": true, 16 } 17 18 // FieldType describes the AST type of a field in a struct. 19 type FieldType int 20 21 // The constants are the AST types that this code uses. 22 const ( 23 IdentType FieldType = iota 24 StarType 25 ArrayType 26 ) 27 28 // Field represents a field of a struct. 29 type Field struct { 30 Name string 31 Type FieldType 32 TypeName string 33 } 34 35 // TypeVisitor visits ast.Nodes and finds all the types that need code 36 // generation. 37 type TypeVisitor struct { 38 ConcreteTypes map[string][]Field 39 InterfaceTypes map[string]bool 40 } 41 42 // FieldVisitor visits all the fields of a struct found by TypeVisitor and 43 // records them. 44 type FieldVisitor struct { 45 ConcreteTypes map[string][]Field 46 InterfaceTypes map[string]bool 47 Name string 48 } 49 50 // Visit examines a Node and records its type if it matches a type that this 51 // code handles. 52 func (tv *TypeVisitor) Visit(n ast.Node) ast.Visitor { 53 ts, ok := n.(*ast.TypeSpec) 54 if !ok { 55 return tv 56 } 57 58 name := ts.Name.Name 59 switch ts.Type.(type) { 60 case *ast.StructType: 61 if _, ok := tv.ConcreteTypes[name]; !ok { 62 tv.ConcreteTypes[name] = make([]Field, 0) 63 } 64 65 return &FieldVisitor{ 66 ConcreteTypes: tv.ConcreteTypes, 67 InterfaceTypes: tv.InterfaceTypes, 68 Name: name, 69 } 70 case *ast.ArrayType: 71 at := ts.Type.(*ast.ArrayType) 72 elt, ok := at.Elt.(*ast.Ident) 73 if ok { 74 tv.ConcreteTypes[name] = []Field{Field{"elt", ArrayType, elt.Name}} 75 } 76 case *ast.Ident: 77 id := ts.Type.(*ast.Ident) 78 tv.ConcreteTypes[name] = []Field{Field{"value", IdentType, id.Name}} 79 case *ast.InterfaceType: 80 tv.InterfaceTypes[name] = true 81 } 82 83 return tv 84 } 85 86 // Visit examines an ast.Field node and records it name and type. 87 func (fv *FieldVisitor) Visit(n ast.Node) ast.Visitor { 88 f, ok := n.(*ast.Field) 89 if !ok { 90 return fv 91 } 92 93 if len(f.Names) == 0 { 94 return nil 95 } 96 97 name := strings.ToLower(f.Names[0].Name) 98 st := fv.ConcreteTypes 99 switch f.Type.(type) { 100 case *ast.Ident: 101 ident := f.Type.(*ast.Ident) 102 st[fv.Name] = append(st[fv.Name], Field{name, IdentType, ident.Name}) 103 case *ast.StarExpr: 104 star := f.Type.(*ast.StarExpr) 105 ident, ok := star.X.(*ast.Ident) 106 if ok { 107 st[fv.Name] = append(st[fv.Name], Field{name, StarType, ident.Name}) 108 } 109 case *ast.ArrayType: 110 atype := f.Type.(*ast.ArrayType) 111 elt, ok := atype.Elt.(*ast.Ident) 112 if ok { 113 st[fv.Name] = append(st[fv.Name], Field{name, ArrayType, elt.Name}) 114 } 115 default: 116 return fv 117 } 118 119 return &TypeVisitor{ 120 ConcreteTypes: fv.ConcreteTypes, 121 InterfaceTypes: fv.InterfaceTypes, 122 } 123 } 124 125 // FuncReceiverWalker holds information about the receiver types in the AST. 126 type FuncReceiverWalker struct { 127 types map[string]bool 128 name string 129 } 130 131 // Visit handles a node in the AST and records the receiver type, if any. 132 func (fw *FuncReceiverWalker) Visit(n ast.Node) ast.Visitor { 133 fd, ok := n.(*ast.FuncDecl) 134 if !ok { 135 return fw 136 } 137 138 if fd.Name.Name != fw.name { 139 return fw 140 } 141 142 // Record the name of the type of the first receiver. 143 if len(fd.Recv.List) == 0 { 144 return fw 145 } 146 147 field := fd.Recv.List[0] 148 ident, ok := field.Type.(*ast.Ident) 149 if !ok { 150 return fw 151 } 152 153 fw.types[ident.Name] = true 154 return fw 155 } 156 157 // Constant represents a constant value in the file. 158 type Constant struct { 159 Name string 160 Value int 161 } 162 163 // ConstantVisitor stores a list of constants from the file. 164 type ConstantVisitor struct { 165 Constants []Constant 166 } 167 168 // Visit handles an ast.Node and records it if this node is a constant. 169 func (tv *ConstantVisitor) Visit(n ast.Node) ast.Visitor { 170 vs, ok := n.(*ast.ValueSpec) 171 if !ok { 172 return tv 173 } 174 175 if len(vs.Names) == 0 { 176 return tv 177 } 178 179 ident := vs.Names[0] 180 if ident.Name == "_" { 181 return tv 182 } 183 184 if ident.Obj == nil || ident.Obj.Data == nil || ident.Obj.Kind != ast.Con { 185 return tv 186 } 187 188 value, ok := ident.Obj.Data.(int) 189 if !ok { 190 return tv 191 } 192 193 // Turn the constant name into a C++ constant name. 194 name := "k" + strings.Title(ident.Name) 195 196 tv.Constants = append(tv.Constants, Constant{name, value}) 197 return tv 198 } 199 200 func ParseAuthAst(binaryFile, astFile string) *CppGenerator { 201 tset := token.NewFileSet() 202 tf, err := parser.ParseFile(tset, binaryFile, nil, 0) 203 if err != nil { 204 log.Fatal(err) 205 } 206 207 constantVisitor := &ConstantVisitor{ 208 Constants: make([]Constant, 0), 209 } 210 211 ast.Walk(constantVisitor, tf) 212 213 fset := token.NewFileSet() 214 f, err := parser.ParseFile(fset, astFile, nil, 0) 215 if err != nil { 216 log.Fatal(err) 217 } 218 219 tv := &TypeVisitor{ 220 ConcreteTypes: make(map[string][]Field), 221 InterfaceTypes: make(map[string]bool), 222 } 223 ast.Walk(tv, f) 224 225 formWalker := &FuncReceiverWalker{ 226 types: make(map[string]bool), 227 name: "isForm", 228 } 229 ast.Walk(formWalker, f) 230 231 termWalker := &FuncReceiverWalker{ 232 types: make(map[string]bool), 233 name: "isTerm", 234 } 235 ast.Walk(termWalker, f) 236 237 return &CppGenerator{ 238 Constants: constantVisitor.Constants, 239 Types: tv.ConcreteTypes, 240 Interfaces: tv.InterfaceTypes, 241 FormTypes: formWalker.types, 242 TermTypes: termWalker.types, 243 } 244 }