blob: 41ab2b26fc1b43d5898b2effdc00c7aab6f61847 [file] [log] [blame]
// Copyright 2019 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 codegen
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"text/template"
fidl "go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
cpp "go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen_cpp"
)
type FidlGenerator struct {
tmpls *template.Template
}
func NewFidlGenerator() *FidlGenerator {
tmpls := template.New("LibFuzzer").Funcs(template.FuncMap{
"Kinds": func() interface{} { return cpp.Kinds },
"Eq": func(a interface{}, b interface{}) bool { return a == b },
"NEq": func(a interface{}, b interface{}) bool { return a != b },
"DoubleColonToUnderscore": func(s string) string {
s2 := strings.ReplaceAll(s, "::", "_")
// Drop any leading "::" => "_".
if len(s2) > 0 && s2[0] == '_' {
return s2[1:]
}
return s2
},
"Protocols": protocols,
})
template.Must(tmpls.Parse(tmplBits))
template.Must(tmpls.Parse(tmplEnum))
template.Must(tmpls.Parse(tmplHeader))
template.Must(tmpls.Parse(tmplSource))
template.Must(tmpls.Parse(tmplStruct))
template.Must(tmpls.Parse(tmplTable))
template.Must(tmpls.Parse(tmplUnion))
return &FidlGenerator{
tmpls: tmpls,
}
}
// GenerateHeader generates the C++ libfuzzer traits for FIDL types.
func (gen *FidlGenerator) GenerateHeader(wr io.Writer, tree cpp.Root) error {
return gen.tmpls.ExecuteTemplate(wr, "Header", tree)
}
// GenerateSource generates the C++ fuzzer implementation protocols in the FIDL file.
func (gen *FidlGenerator) GenerateSource(wr io.Writer, tree cpp.Root) error {
return gen.tmpls.ExecuteTemplate(wr, "Source", tree)
}
// Config is the configuration data passed to the libfuzzer generator.
type Config struct {
// The base file name for files generated by this generator.
OutputBase string
// The directory to which C and C++ includes should be relative.
IncludeBase string
// The path suffix after the library path when referencing includes.
IncludeStem string
}
// GenerateFidl generates all files required for the C++ libfuzzer code.
func (gen FidlGenerator) GenerateFidl(fidl fidl.Root, config *Config, clangFormatPath string) error {
tree := cpp.CompileLibFuzzer(fidl)
prepareTree(fidl.Name, config.IncludeStem, &tree)
headerPath := config.OutputBase + ".h"
sourcePath := config.OutputBase + ".cc"
baseDir := filepath.Dir(config.OutputBase)
if err := os.MkdirAll(baseDir, os.ModePerm); err != nil {
return err
}
headerFile, err := os.Create(headerPath)
if err != nil {
return err
}
headerFormatterPipe, err := cpp.NewClangFormatter(clangFormatPath).FormatPipe(headerFile)
if err != nil {
return err
}
defer headerFormatterPipe.Close()
if err := gen.GenerateHeader(headerFormatterPipe, tree); err != nil {
return err
}
// Note that if the FIDL library defines no protocols, this will produce an empty file.
sourceFile, err := os.Create(sourcePath)
if err != nil {
return err
}
sourceFormatterPipe, err := cpp.NewClangFormatter(clangFormatPath).FormatPipe(sourceFile)
if err != nil {
return err
}
defer sourceFormatterPipe.Close()
if len(fidl.Protocols) > 0 {
mthdCount := 0
for _, protocol := range fidl.Protocols {
mthdCount += len(protocol.Methods)
}
if mthdCount == 0 {
return fmt.Errorf("No non-empty protocols in FIDL library: %s", string(fidl.Name))
}
if err := gen.GenerateSource(sourceFormatterPipe, tree); err != nil {
return err
}
}
return nil
}
func prepareTree(name fidl.EncodedLibraryIdentifier, includeStem string, tree *cpp.Root) {
pkgPath := strings.Replace(string(name), ".", "/", -1)
tree.PrimaryHeader = pkgPath + "/" + includeStem + ".h"
tree.IncludeStem = includeStem
tree.Headers = []string{pkgPath}
}
func protocols(decls []cpp.Decl) []*cpp.Protocol {
protocols := make([]*cpp.Protocol, 0, len(decls))
for _, decl := range decls {
if protocol, ok := decl.(*cpp.Protocol); ok {
protocols = append(protocols, protocol)
}
}
return protocols
}