diff options
Diffstat (limited to 'nexgb/xgbgen/field.go')
-rw-r--r-- | nexgb/xgbgen/field.go | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/nexgb/xgbgen/field.go b/nexgb/xgbgen/field.go new file mode 100644 index 0000000..d6957b7 --- /dev/null +++ b/nexgb/xgbgen/field.go @@ -0,0 +1,398 @@ +package main + +import ( + "fmt" + "log" + "strings" +) + +// Field corresponds to any field described in an XML protocol description +// file. This includes struct fields, union fields, request fields, +// reply fields and so on. +// To make code generation easier, fields that have types are also stored. +// Note that not all fields support all methods defined in this interface. +// For instance, a padding field does not have a source name. +type Field interface { + // Initialize sets up the source name of this field. + Initialize(p *Protocol) + + // SrcName is the Go source name of this field. + SrcName() string + + // XmlName is the name of this field from the XML file. + XmlName() string + + // SrcType is the Go source type name of this field. + SrcType() string + + // Size returns an expression that computes the size (in bytes) + // of this field. + Size() Size + + // Define writes the Go code to declare this field (in a struct definition). + Define(c *Context) + + // Read writes the Go code to convert a byte slice to a Go value + // of this field. + // 'prefix' is the prefix of the name of the Go value. + Read(c *Context, prefix string) + + // Write writes the Go code to convert a Go value to a byte slice of + // this field. + // 'prefix' is the prefix of the name of the Go value. + Write(c *Context, prefix string) +} + +func (pad *PadField) Initialize(p *Protocol) {} + +// PadField represents any type of padding. It is omitted from +// definitions, but is used in Read/Write to increment the buffer index. +// It is also used in size calculation. +type PadField struct { + Bytes uint + Align uint16 +} + +func (p *PadField) SrcName() string { + panic("illegal to take source name of a pad field") +} + +func (p *PadField) XmlName() string { + panic("illegal to take XML name of a pad field") +} + +func (f *PadField) SrcType() string { + panic("it is illegal to call SrcType on a PadField field") +} + +func (p *PadField) Size() Size { + if p.Align > 0 { + return newFixedSize(uint(p.Align), false) + } else { + return newFixedSize(p.Bytes, true) + } +} + +type RequiredStartAlign struct { +} + +func (f *RequiredStartAlign) Initialize(p *Protocol) {} + +func (f *RequiredStartAlign) SrcName() string { + panic("illegal to take source name of a required_start_align field") +} + +func (f *RequiredStartAlign) XmlName() string { + panic("illegal to take XML name of a required_start_align field") +} + +func (f *RequiredStartAlign) SrcType() string { + panic("it is illegal to call SrcType on a required_start_align field") +} + +func (f *RequiredStartAlign) Size() Size { + return newFixedSize(0, true) +} + +func (f *RequiredStartAlign) Define(c *Context) {} + +func (f *RequiredStartAlign) Read(c *Context, prefix string) {} +func (f *RequiredStartAlign) Write(c *Context, prefix string) {} + +// SingleField represents most of the fields in an XML protocol description. +// It corresponds to any single value. +type SingleField struct { + srcName string + xmlName string + Type Type +} + +func (f *SingleField) Initialize(p *Protocol) { + f.srcName = SrcName(p, f.XmlName()) + f.Type = f.Type.(*Translation).RealType(p) +} + +func (f *SingleField) SrcName() string { + if f.srcName == "Bytes" { + return "Bytes_" + } + return f.srcName +} + +func (f *SingleField) XmlName() string { + return f.xmlName +} + +func (f *SingleField) SrcType() string { + return f.Type.SrcName() +} + +func (f *SingleField) Size() Size { + return f.Type.Size() +} + +// ListField represents a list of values. +type ListField struct { + srcName string + xmlName string + Type Type + LengthExpr Expression +} + +func (f *ListField) SrcName() string { + return f.srcName +} + +func (f *ListField) XmlName() string { + return f.xmlName +} + +func (f *ListField) SrcType() string { + if strings.ToLower(f.Type.XmlName()) == "char" { + return fmt.Sprintf("string") + } + return fmt.Sprintf("[]%s", f.Type.SrcName()) +} + +// Length computes the *number* of values in a list. +// If this ListField does not have any length expression, we throw our hands +// up and simply compute the 'len' of the field name of this list. +func (f *ListField) Length() Size { + if f.LengthExpr == nil { + return newExpressionSize(&Function{ + Name: "len", + Expr: &FieldRef{ + Name: f.SrcName(), + }, + }, true) + } + return newExpressionSize(f.LengthExpr, true) +} + +// Size computes the *size* of a list (in bytes). +// It it typically a simple matter of multiplying the length of the list by +// the size of the type of the list. +// But if it's a list of struct where the struct has a list field, we use a +// special function written in go_struct.go to compute the size (since the +// size in this case can only be computed recursively). +func (f *ListField) Size() Size { + elsz := f.Type.Size() + simpleLen := &Padding{ + Expr: newBinaryOp("*", f.Length().Expression, elsz.Expression), + } + + switch field := f.Type.(type) { + case *Struct: + if field.HasList() { + sizeFun := &Function{ + Name: fmt.Sprintf("%sListSize", f.Type.SrcName()), + Expr: &FieldRef{Name: f.SrcName()}, + } + return newExpressionSize(sizeFun, elsz.exact) + } else { + return newExpressionSize(simpleLen, elsz.exact) + } + case *Union: + return newExpressionSize(simpleLen, elsz.exact) + case *Base: + return newExpressionSize(simpleLen, elsz.exact) + case *Resource: + return newExpressionSize(simpleLen, elsz.exact) + case *TypeDef: + return newExpressionSize(simpleLen, elsz.exact) + default: + log.Panicf("Cannot compute list size with type '%T'.", f.Type) + } + panic("unreachable") +} + +func (f *ListField) Initialize(p *Protocol) { + f.srcName = SrcName(p, f.XmlName()) + f.Type = f.Type.(*Translation).RealType(p) + if f.LengthExpr != nil { + f.LengthExpr.Initialize(p) + } +} + +// LocalField is exactly the same as a regular SingleField, except it isn't +// sent over the wire. (i.e., it's probably used to compute an ExprField). +type LocalField struct { + *SingleField +} + +// ExprField is a field that is not parameterized, but is computed from values +// of other fields. +type ExprField struct { + srcName string + xmlName string + Type Type + Expr Expression +} + +func (f *ExprField) SrcName() string { + return f.srcName +} + +func (f *ExprField) XmlName() string { + return f.xmlName +} + +func (f *ExprField) SrcType() string { + return f.Type.SrcName() +} + +func (f *ExprField) Size() Size { + return f.Type.Size() +} + +func (f *ExprField) Initialize(p *Protocol) { + f.srcName = SrcName(p, f.XmlName()) + f.Type = f.Type.(*Translation).RealType(p) + f.Expr.Initialize(p) +} + +// ValueField represents two fields in one: a mask and a list of 4-byte +// integers. The mask specifies which kinds of values are in the list. +// (i.e., See ConfigureWindow, CreateWindow, ChangeWindowAttributes, etc.) +type ValueField struct { + Parent interface{} + MaskType Type + MaskName string + ListName string +} + +func (f *ValueField) SrcName() string { + panic("it is illegal to call SrcName on a ValueField field") +} + +func (f *ValueField) XmlName() string { + panic("it is illegal to call XmlName on a ValueField field") +} + +func (f *ValueField) SrcType() string { + return f.MaskType.SrcName() +} + +// Size computes the size in bytes of the combination of the mask and list +// in this value field. +// The expression to compute this looks complicated, but it's really just +// the number of bits set in the mask multiplied 4 (and padded of course). +func (f *ValueField) Size() Size { + maskSize := f.MaskType.Size() + listSize := newExpressionSize(&Function{ + Name: "xgb.Pad", + Expr: &BinaryOp{ + Op: "*", + Expr1: &Value{v: 4}, + Expr2: &PopCount{ + Expr: &Function{ + Name: "int", + Expr: &FieldRef{ + Name: f.MaskName, + }, + }, + }, + }, + }, true) + return maskSize.Add(listSize) +} + +func (f *ValueField) ListLength() Size { + return newExpressionSize(&PopCount{ + Expr: &Function{ + Name: "int", + Expr: &FieldRef{ + Name: f.MaskName, + }, + }, + }, true) +} + +func (f *ValueField) Initialize(p *Protocol) { + f.MaskType = f.MaskType.(*Translation).RealType(p) + f.MaskName = SrcName(p, f.MaskName) + f.ListName = SrcName(p, f.ListName) +} + +// SwitchField represents a 'switch' element in the XML protocol description +// file. +// Currently we translate this to a slice of uint32 and let the user sort +// through it. +type SwitchField struct { + xmlName string + Name string + MaskName string + Expr Expression + Bitcases []*Bitcase +} + +func (f *SwitchField) SrcName() string { + return f.Name +} + +func (f *SwitchField) XmlName() string { + return f.xmlName +} + +func (f *SwitchField) SrcType() string { + return "[]uint32" +} + +func (f *SwitchField) Size() Size { + // TODO: size expression used here is not correct unless every element of + // the switch is 32 bit long. This assumption holds for xproto but may not + // hold for other protocols (xkb?) + + listSize := newExpressionSize(&Function{ + Name: "xgb.Pad", + Expr: &BinaryOp{ + Op: "*", + Expr1: &Value{v: 4}, + Expr2: &PopCount{ + Expr: &Function{ + Name: "int", + Expr: &FieldRef{ + Name: f.MaskName, + }, + }, + }, + }, + }, true) + + return listSize +} + +func (f *SwitchField) ListLength() Size { + return newExpressionSize(&PopCount{ + Expr: &Function{ + Name: "int", + Expr: &FieldRef{ + Name: f.MaskName, + }, + }, + }, true) +} + +func (f *SwitchField) Initialize(p *Protocol) { + f.xmlName = f.Name + f.Name = SrcName(p, f.Name) + f.Expr.Initialize(p) + fieldref, ok := f.Expr.(*FieldRef) + if !ok { + panic("switch field's expression not a fieldref") + } + f.MaskName = SrcName(p, fieldref.Name) + for _, bitcase := range f.Bitcases { + bitcase.Expr.Initialize(p) + for _, field := range bitcase.Fields { + field.Initialize(p) + } + } +} + +// Bitcase represents a single bitcase inside a switch expression. +// It is not currently used. (i.e., it's XKB voodoo.) +type Bitcase struct { + Fields []Field + Expr Expression +} |