aboutsummaryrefslogtreecommitdiff
path: root/nexgb/xgbgen/field.go
diff options
context:
space:
mode:
Diffstat (limited to 'nexgb/xgbgen/field.go')
-rw-r--r--nexgb/xgbgen/field.go398
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
+}