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  }