| // 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" |
| fidlgen "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, |
| "UnwrapProtocol": unwrapProtocol, |
| }) |
| |
| template.Must(tmpls.Parse(tmplBits)) |
| template.Must(tmpls.Parse(tmplDecoderEncoder)) |
| template.Must(tmpls.Parse(tmplDecoderEncoderHeader)) |
| template.Must(tmpls.Parse(tmplDecoderEncoderSource)) |
| template.Must(tmpls.Parse(tmplEnum)) |
| template.Must(tmpls.Parse(tmplHeader)) |
| template.Must(tmpls.Parse(tmplProtocolDecoderEncoders)) |
| 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) |
| } |
| |
| // GenerateDecoderEncoderHeader generates the C++ libfuzzer traits for FIDL types. |
| func (gen *FidlGenerator) GenerateDecoderEncoderHeader(wr io.Writer, tree cpp.Root) error { |
| return gen.tmpls.ExecuteTemplate(wr, "DecoderEncoderHeader", tree) |
| } |
| |
| // GenerateDecoderEncoderSource generates the C++ fuzzer implementation protocols in the FIDL file. |
| func (gen *FidlGenerator) GenerateDecoderEncoderSource(wr io.Writer, tree cpp.Root) error { |
| return gen.tmpls.ExecuteTemplate(wr, "DecoderEncoderSource", 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) |
| |
| baseDir := filepath.Dir(config.OutputBase) |
| if err := os.MkdirAll(baseDir, os.ModePerm); err != nil { |
| return err |
| } |
| |
| if err := gen.GenerateFuzzer(fidl, tree, config, clangFormatPath); err != nil { |
| return err |
| } |
| |
| if err := gen.GenerateDecoderEncoders(fidl, tree, config, clangFormatPath); err != nil { |
| return err |
| } |
| |
| return nil |
| } |
| |
| func (gen FidlGenerator) GenerateFuzzer(fidl fidl.Root, tree cpp.Root, config *Config, clangFormatPath string) error { |
| headerPath := config.OutputBase + ".h" |
| sourcePath := config.OutputBase + ".cc" |
| |
| headerFile, err := fidlgen.NewLazyWriter(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 := fidlgen.NewLazyWriter(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 (gen FidlGenerator) GenerateDecoderEncoders(fidl fidl.Root, tree cpp.Root, config *Config, clangFormatPath string) error { |
| headerPath := config.OutputBase + "_decode_encode.h" |
| sourcePath := config.OutputBase + "_decode_encode.cc" |
| |
| headerFile, err := fidlgen.NewLazyWriter(headerPath) |
| if err != nil { |
| return err |
| } |
| |
| headerFormatterPipe, err := cpp.NewClangFormatter(clangFormatPath).FormatPipe(headerFile) |
| if err != nil { |
| return err |
| } |
| defer headerFormatterPipe.Close() |
| |
| if err := gen.GenerateDecoderEncoderHeader(headerFormatterPipe, tree); err != nil { |
| return err |
| } |
| |
| // Note that if the FIDL library defines no protocols, this will produce an empty file. |
| sourceFile, err := fidlgen.NewLazyWriter(sourcePath) |
| if err != nil { |
| return err |
| } |
| |
| sourceFormatterPipe, err := cpp.NewClangFormatter(clangFormatPath).FormatPipe(sourceFile) |
| if err != nil { |
| return err |
| } |
| defer sourceFormatterPipe.Close() |
| |
| return gen.GenerateDecoderEncoderSource(sourceFormatterPipe, tree) |
| } |
| |
| 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 := unwrapProtocol(decl); protocol != nil { |
| protocols = append(protocols, protocol) |
| } |
| } |
| return protocols |
| } |
| |
| func unwrapProtocol(decl cpp.Decl) *cpp.Protocol { |
| protocol, _ := decl.(*cpp.Protocol) |
| return protocol |
| } |