blob: 4e27659039ae49efadb594db7cfdabb444f6cb39 [file] [log] [blame] [edit]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package c
import (
"embed"
"fmt"
"path/filepath"
"strconv"
"strings"
"text/template"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
"go.fuchsia.dev/fuchsia/zircon/tools/zither"
)
//go:embed templates/*
var templates embed.FS
// Generator provides C data layout bindings.
type Generator struct {
fidlgen.Generator
outputNamespaceOverride string
}
func NewGenerator(formatter fidlgen.Formatter, outputNamespaceOverride string) *Generator {
gen := fidlgen.NewGenerator("CTemplates", templates, formatter, template.FuncMap{
"LowerCaseWithUnderscores": LowerCaseWithUnderscores,
"UpperCaseWithUnderscores": UpperCaseWithUnderscores,
"Append": Append,
"PrimitiveTypeName": PrimitiveTypeName,
"HeaderGuard": func(summary zither.FileSummary) string {
return zither.CHeaderGuard(summary, "c", outputNamespaceOverride)
},
"StandardIncludes": StandardIncludes,
"TypeName": TypeName,
"ConstMemberName": ConstMemberName,
"ConstValue": ConstValue,
"EnumMemberValue": EnumMemberValue,
"BitsMemberValue": BitsMemberValue,
"DescribeType": DescribeType,
})
return &Generator{*gen, outputNamespaceOverride}
}
func (gen Generator) DeclOrder() zither.DeclOrder { return zither.DependencyDeclOrder }
func (gen Generator) DeclCallback(zither.Decl) {}
func (gen *Generator) Generate(summary zither.LibrarySummary, outputDir string) ([]string, error) {
var outputs []string
for _, summary := range summary.Files {
output := filepath.Join(outputDir, zither.CHeaderPath(summary, "c", gen.outputNamespaceOverride))
if err := gen.GenerateFile(output, "GenerateCFile", summary); err != nil {
return nil, err
}
outputs = append(outputs, output)
}
readme := filepath.Join(outputDir, "README.md")
if err := zither.WriteReadme(readme, summary); err != nil {
return nil, err
}
outputs = append(outputs, readme)
return outputs, nil
}
//
// Template functions.
//
// LowerCaseWithUnderscores is a wrapper around the zither-defined utility that
// also includes the library name.
func LowerCaseWithUnderscores(el zither.Element) string {
decl, ok := el.(zither.Decl)
if ok {
libParts := decl.GetName().LibraryName().Parts()
prefix := fidlgen.ToSnakeCase(strings.Join(libParts, "_"))
return prefix + "_" + zither.LowerCaseWithUnderscores(el)
}
return zither.LowerCaseWithUnderscores(el)
}
// UpperCaseWithUnderscores is a wrapper around the zither-defined utility that
// also includes the library name.
func UpperCaseWithUnderscores(el zither.Element) string {
return strings.ToUpper(LowerCaseWithUnderscores(el))
}
func Append(s, t string) string { return s + t }
// PrimitiveTypeName returns the C type name for a given a primitive FIDL type.
func PrimitiveTypeName(typ fidlgen.PrimitiveSubtype) string {
switch typ {
case fidlgen.Bool:
return "bool"
case fidlgen.ZxExperimentalUchar:
return "char"
case fidlgen.ZxExperimentalUsize64:
return "size_t"
case fidlgen.ZxExperimentalUintptr64:
return "uintptr_t"
case fidlgen.Int8, fidlgen.Int16, fidlgen.Int32, fidlgen.Int64,
fidlgen.Uint8, fidlgen.Uint16, fidlgen.Uint32, fidlgen.Uint64:
return string(typ) + "_t"
default:
panic(fmt.Errorf("unrecognized primitive type: %s", typ))
}
}
func TypeName(decl zither.Decl) string {
return LowerCaseWithUnderscores(decl) + "_t"
}
func ConstMemberName(parent zither.Decl, member zither.Member) string {
return UpperCaseWithUnderscores(parent) + "_" + UpperCaseWithUnderscores(member)
}
// StandardIncludes gives the list of language standard headers used by a file.
func StandardIncludes(summary zither.FileSummary) []string {
var includes []string
for _, kind := range summary.TypeKinds() {
switch kind {
case zither.TypeKindInteger:
includes = append(includes, "stdint.h")
case zither.TypeKindBool:
includes = append(includes, "stdbool.h")
case zither.TypeKindSize:
includes = append(includes, "stddef.h")
}
}
return includes
}
// ConstValue returns the right-hand side of a generated C "constant" declaration.
func ConstValue(c zither.Const) string {
if c.Element != nil {
if c.Element.Member != nil {
return ConstMemberName(c.Element.Decl, c.Element.Member)
}
if c.Kind == zither.TypeKindBits {
val, err := strconv.Atoi(c.Value)
if err != nil {
panic(fmt.Sprintf("%s has malformed integral value: %s", c.Name, err))
}
return fmt.Sprintf("((%s)(%su))", TypeName(c.Element.Decl), fmt.Sprintf("%#b", val))
}
return UpperCaseWithUnderscores(c.Element.Decl)
}
switch c.Kind {
case zither.TypeKindString:
return fmt.Sprintf("%q", c.Value)
case zither.TypeKindBool, zither.TypeKindInteger, zither.TypeKindSize:
fidlType := fidlgen.PrimitiveSubtype(c.Type)
cType := PrimitiveTypeName(fidlType)
val := c.Value
if c.Kind == zither.TypeKindInteger || c.Kind == zither.TypeKindSize {
// In the case of an signed type, the unsigned-ness will be
// explicitly cast away.
val += "u"
}
return fmt.Sprintf("((%s)(%s))", cType, val)
case zither.TypeKindEnum, zither.TypeKindBits:
// Enum and bits constants should have been handled above.
panic(fmt.Sprintf("enum and bits constants must be given by an `Element` value: %#v", c))
default:
panic(fmt.Sprintf("%s has unknown constant kind: %s", c.Name, c.Type))
}
}
// EnumMemberValue returns the value of a generated C "enum" member.
func EnumMemberValue(enum zither.Enum, member zither.EnumMember) string {
return fmt.Sprintf("((%s)(%su))", TypeName(enum), member.Value)
}
// BitsMemberValue returns the value of a generated C bitset member.
func BitsMemberValue(bits zither.Bits, member zither.BitsMember) string {
return fmt.Sprintf("((%s)(1u << %d))", TypeName(bits), member.Index)
}
// TypeInfo gives a basic description of a type, accounting for array nesting.
//
// For example, the type `uint32[4][5][6]` is encoded as
// `TypeInfo{"uint32", []int{4, 5, 6}}`.
type TypeInfo struct {
// Type is the underlying type, modulo array nesting.
Type string
// ArrayCounts gives the successive element counts within any array
// nesting.
ArrayCounts []int
}
// ArraySuffix is the suffix to append to the member name to indicate any array
// nesting encoded within the type. If the type is not an array, this returns an
// empty string.
func (info TypeInfo) ArraySuffix() string {
suffix := ""
for _, count := range info.ArrayCounts {
suffix += fmt.Sprintf("[%d]", count)
}
return suffix
}
func (info TypeInfo) String() string {
return info.Type + info.ArraySuffix()
}
func DescribeType(desc zither.TypeDescriptor) TypeInfo {
switch desc.Kind {
case zither.TypeKindBool, zither.TypeKindInteger, zither.TypeKindSize:
return TypeInfo{Type: PrimitiveTypeName(fidlgen.PrimitiveSubtype(desc.Type))}
case zither.TypeKindEnum, zither.TypeKindBits, zither.TypeKindStruct, zither.TypeKindAlias, zither.TypeKindHandle:
return TypeInfo{Type: TypeName(desc.Decl)}
case zither.TypeKindArray:
info := DescribeType(*desc.ElementType)
info.ArrayCounts = append(info.ArrayCounts, *desc.ElementCount)
return info
case zither.TypeKindStringArray:
return TypeInfo{Type: "char", ArrayCounts: []int{*desc.ElementCount}}
case zither.TypeKindPointer:
info := DescribeType(*desc.ElementType)
info.Type += "*"
return info
case zither.TypeKindVoidPointer:
return TypeInfo{Type: "void*"}
default:
panic(fmt.Sprintf("unsupported type kind: %v", desc.Kind))
}
}