gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

format.go (6904B)


      1 package btf
      2 
      3 import (
      4 	"errors"
      5 	"fmt"
      6 	"strings"
      7 )
      8 
      9 var errNestedTooDeep = errors.New("nested too deep")
     10 
     11 // GoFormatter converts a Type to Go syntax.
     12 //
     13 // A zero GoFormatter is valid to use.
     14 type GoFormatter struct {
     15 	w strings.Builder
     16 
     17 	// Types present in this map are referred to using the given name if they
     18 	// are encountered when outputting another type.
     19 	Names map[Type]string
     20 
     21 	// Identifier is called for each field of struct-like types. By default the
     22 	// field name is used as is.
     23 	Identifier func(string) string
     24 
     25 	// EnumIdentifier is called for each element of an enum. By default the
     26 	// name of the enum type is concatenated with Identifier(element).
     27 	EnumIdentifier func(name, element string) string
     28 }
     29 
     30 // TypeDeclaration generates a Go type declaration for a BTF type.
     31 func (gf *GoFormatter) TypeDeclaration(name string, typ Type) (string, error) {
     32 	gf.w.Reset()
     33 	if err := gf.writeTypeDecl(name, typ); err != nil {
     34 		return "", err
     35 	}
     36 	return gf.w.String(), nil
     37 }
     38 
     39 func (gf *GoFormatter) identifier(s string) string {
     40 	if gf.Identifier != nil {
     41 		return gf.Identifier(s)
     42 	}
     43 
     44 	return s
     45 }
     46 
     47 func (gf *GoFormatter) enumIdentifier(name, element string) string {
     48 	if gf.EnumIdentifier != nil {
     49 		return gf.EnumIdentifier(name, element)
     50 	}
     51 
     52 	return name + gf.identifier(element)
     53 }
     54 
     55 // writeTypeDecl outputs a declaration of the given type.
     56 //
     57 // It encodes https://golang.org/ref/spec#Type_declarations:
     58 //
     59 //     type foo struct { bar uint32; }
     60 //     type bar int32
     61 func (gf *GoFormatter) writeTypeDecl(name string, typ Type) error {
     62 	if name == "" {
     63 		return fmt.Errorf("need a name for type %s", typ)
     64 	}
     65 
     66 	switch v := skipQualifiers(typ).(type) {
     67 	case *Enum:
     68 		fmt.Fprintf(&gf.w, "type %s ", name)
     69 		switch v.Size {
     70 		case 1:
     71 			gf.w.WriteString("int8")
     72 		case 2:
     73 			gf.w.WriteString("int16")
     74 		case 4:
     75 			gf.w.WriteString("int32")
     76 		case 8:
     77 			gf.w.WriteString("int64")
     78 		default:
     79 			return fmt.Errorf("%s: invalid enum size %d", typ, v.Size)
     80 		}
     81 
     82 		if len(v.Values) == 0 {
     83 			return nil
     84 		}
     85 
     86 		gf.w.WriteString("; const ( ")
     87 		for _, ev := range v.Values {
     88 			id := gf.enumIdentifier(name, ev.Name)
     89 			fmt.Fprintf(&gf.w, "%s %s = %d; ", id, name, ev.Value)
     90 		}
     91 		gf.w.WriteString(")")
     92 
     93 		return nil
     94 
     95 	default:
     96 		fmt.Fprintf(&gf.w, "type %s ", name)
     97 		return gf.writeTypeLit(v, 0)
     98 	}
     99 }
    100 
    101 // writeType outputs the name of a named type or a literal describing the type.
    102 //
    103 // It encodes https://golang.org/ref/spec#Types.
    104 //
    105 //     foo                  (if foo is a named type)
    106 //     uint32
    107 func (gf *GoFormatter) writeType(typ Type, depth int) error {
    108 	typ = skipQualifiers(typ)
    109 
    110 	name := gf.Names[typ]
    111 	if name != "" {
    112 		gf.w.WriteString(name)
    113 		return nil
    114 	}
    115 
    116 	return gf.writeTypeLit(typ, depth)
    117 }
    118 
    119 // writeTypeLit outputs a literal describing the type.
    120 //
    121 // The function ignores named types.
    122 //
    123 // It encodes https://golang.org/ref/spec#TypeLit.
    124 //
    125 //     struct { bar uint32; }
    126 //     uint32
    127 func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error {
    128 	depth++
    129 	if depth > maxTypeDepth {
    130 		return errNestedTooDeep
    131 	}
    132 
    133 	var err error
    134 	switch v := skipQualifiers(typ).(type) {
    135 	case *Int:
    136 		gf.writeIntLit(v)
    137 
    138 	case *Enum:
    139 		gf.w.WriteString("int32")
    140 
    141 	case *Typedef:
    142 		err = gf.writeType(v.Type, depth)
    143 
    144 	case *Array:
    145 		fmt.Fprintf(&gf.w, "[%d]", v.Nelems)
    146 		err = gf.writeType(v.Type, depth)
    147 
    148 	case *Struct:
    149 		err = gf.writeStructLit(v.Size, v.Members, depth)
    150 
    151 	case *Union:
    152 		// Always choose the first member to represent the union in Go.
    153 		err = gf.writeStructLit(v.Size, v.Members[:1], depth)
    154 
    155 	case *Datasec:
    156 		err = gf.writeDatasecLit(v, depth)
    157 
    158 	default:
    159 		return fmt.Errorf("type %T: %w", v, ErrNotSupported)
    160 	}
    161 
    162 	if err != nil {
    163 		return fmt.Errorf("%s: %w", typ, err)
    164 	}
    165 
    166 	return nil
    167 }
    168 
    169 func (gf *GoFormatter) writeIntLit(i *Int) {
    170 	// NB: Encoding.IsChar is ignored.
    171 	if i.Encoding.IsBool() && i.Size == 1 {
    172 		gf.w.WriteString("bool")
    173 		return
    174 	}
    175 
    176 	bits := i.Size * 8
    177 	if i.Encoding.IsSigned() {
    178 		fmt.Fprintf(&gf.w, "int%d", bits)
    179 	} else {
    180 		fmt.Fprintf(&gf.w, "uint%d", bits)
    181 	}
    182 }
    183 
    184 func (gf *GoFormatter) writeStructLit(size uint32, members []Member, depth int) error {
    185 	gf.w.WriteString("struct { ")
    186 
    187 	prevOffset := uint32(0)
    188 	skippedBitfield := false
    189 	for i, m := range members {
    190 		if m.BitfieldSize > 0 {
    191 			skippedBitfield = true
    192 			continue
    193 		}
    194 
    195 		offset := m.Offset.Bytes()
    196 		if n := offset - prevOffset; skippedBitfield && n > 0 {
    197 			fmt.Fprintf(&gf.w, "_ [%d]byte /* unsupported bitfield */; ", n)
    198 		} else {
    199 			gf.writePadding(n)
    200 		}
    201 
    202 		size, err := Sizeof(m.Type)
    203 		if err != nil {
    204 			return fmt.Errorf("field %d: %w", i, err)
    205 		}
    206 		prevOffset = offset + uint32(size)
    207 
    208 		if err := gf.writeStructField(m, depth); err != nil {
    209 			return fmt.Errorf("field %d: %w", i, err)
    210 		}
    211 	}
    212 
    213 	gf.writePadding(size - prevOffset)
    214 	gf.w.WriteString("}")
    215 	return nil
    216 }
    217 
    218 func (gf *GoFormatter) writeStructField(m Member, depth int) error {
    219 	if m.BitfieldSize > 0 {
    220 		return fmt.Errorf("bitfields are not supported")
    221 	}
    222 	if m.Offset%8 != 0 {
    223 		return fmt.Errorf("unsupported offset %d", m.Offset)
    224 	}
    225 
    226 	if m.Name == "" {
    227 		// Special case a nested anonymous union like
    228 		//     struct foo { union { int bar; int baz }; }
    229 		// by replacing the whole union with its first member.
    230 		union, ok := m.Type.(*Union)
    231 		if !ok {
    232 			return fmt.Errorf("anonymous fields are not supported")
    233 
    234 		}
    235 
    236 		if len(union.Members) == 0 {
    237 			return errors.New("empty anonymous union")
    238 		}
    239 
    240 		depth++
    241 		if depth > maxTypeDepth {
    242 			return errNestedTooDeep
    243 		}
    244 
    245 		m := union.Members[0]
    246 		size, err := Sizeof(m.Type)
    247 		if err != nil {
    248 			return err
    249 		}
    250 
    251 		if err := gf.writeStructField(m, depth); err != nil {
    252 			return err
    253 		}
    254 
    255 		gf.writePadding(union.Size - uint32(size))
    256 		return nil
    257 
    258 	}
    259 
    260 	fmt.Fprintf(&gf.w, "%s ", gf.identifier(m.Name))
    261 
    262 	if err := gf.writeType(m.Type, depth); err != nil {
    263 		return err
    264 	}
    265 
    266 	gf.w.WriteString("; ")
    267 	return nil
    268 }
    269 
    270 func (gf *GoFormatter) writeDatasecLit(ds *Datasec, depth int) error {
    271 	gf.w.WriteString("struct { ")
    272 
    273 	prevOffset := uint32(0)
    274 	for i, vsi := range ds.Vars {
    275 		v := vsi.Type.(*Var)
    276 		if v.Linkage != GlobalVar {
    277 			// Ignore static, extern, etc. for now.
    278 			continue
    279 		}
    280 
    281 		if v.Name == "" {
    282 			return fmt.Errorf("variable %d: empty name", i)
    283 		}
    284 
    285 		gf.writePadding(vsi.Offset - prevOffset)
    286 		prevOffset = vsi.Offset + vsi.Size
    287 
    288 		fmt.Fprintf(&gf.w, "%s ", gf.identifier(v.Name))
    289 
    290 		if err := gf.writeType(v.Type, depth); err != nil {
    291 			return fmt.Errorf("variable %d: %w", i, err)
    292 		}
    293 
    294 		gf.w.WriteString("; ")
    295 	}
    296 
    297 	gf.writePadding(ds.Size - prevOffset)
    298 	gf.w.WriteString("}")
    299 	return nil
    300 }
    301 
    302 func (gf *GoFormatter) writePadding(bytes uint32) {
    303 	if bytes > 0 {
    304 		fmt.Fprintf(&gf.w, "_ [%d]byte; ", bytes)
    305 	}
    306 }
    307 
    308 func skipQualifiers(typ Type) Type {
    309 	result := typ
    310 	for depth := 0; depth <= maxTypeDepth; depth++ {
    311 		switch v := (result).(type) {
    312 		case qualifier:
    313 			result = v.qualify()
    314 		default:
    315 			return result
    316 		}
    317 	}
    318 	return &cycle{typ}
    319 }