blob: 06244f9ef189b45db080814f849494c2a487250f [file] [log] [blame]
// Copyright 2018 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 (
"bytes"
"embed"
"fmt"
"text/template"
cpp "go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen_cpp"
)
type Generator struct {
*cpp.Generator
}
type TypedArgument struct {
ArgumentName string
ArgumentValue string
ArgumentType cpp.Type
Pointer bool
Nullable bool
Access bool
MutableAccess bool
}
type closeHandleContext struct {
unique_name_counter int
}
func (c *closeHandleContext) genUniqueName() string {
c.unique_name_counter += 1
return fmt.Sprintf("e_%d", c.unique_name_counter)
}
// closeHandles generates a code snippet to recursively close all handles within
// a wire domain object identified by the expression expr.
//
// exprType is the type of the expression.
func closeHandles(expr string, exprType cpp.Type, context *closeHandleContext) string {
if !exprType.IsResource {
return ""
}
switch exprType.Kind {
case cpp.TypeKinds.Handle, cpp.TypeKinds.Endpoint:
return fmt.Sprintf("%s.reset();", expr)
case cpp.TypeKinds.Array, cpp.TypeKinds.Vector:
// Iterating over array and vector views isn't affected by optionality.
var buf bytes.Buffer
// Use a unique item name to avoid shadowing the name of the collection or
// the name of the item for an ancestor for loop. Shadowing can be detected
// by compiling with -Wshadow.
expr_item_name := context.genUniqueName()
buf.WriteString(fmt.Sprintf("for (auto& %s : %s) {\n", expr_item_name, expr))
buf.WriteString(closeHandles(expr_item_name, *exprType.ElementType, context))
buf.WriteString("\n}\n")
return buf.String()
case cpp.TypeKinds.Union:
// An optional union is wrapped in a `fidl::WireOptional`.
if exprType.Nullable {
return fmt.Sprintf("if (%s.has_value()) { %s->_CloseHandles(); }", expr, expr)
}
return fmt.Sprintf("%s._CloseHandles();", expr)
default:
// An optional struct is wrapped in a `fidl::ObjectView`.
if exprType.Nullable {
return fmt.Sprintf("if (%s != nullptr) { %s->_CloseHandles(); }", expr, expr)
}
return fmt.Sprintf("%s._CloseHandles();", expr)
}
}
// These are the helper functions we inject for use by the templates.
var utilityFuncs = template.FuncMap{
"SyncCallTotalStackSizeV2": func(m cpp.Method) int {
totalSize := 0
if m.Request.ClientAllocationV2.IsStack {
totalSize += m.Request.ClientAllocationV2.StackBytes
}
if m.Response.ClientAllocationV2.IsStack {
totalSize += m.Response.ClientAllocationV2.StackBytes
}
return totalSize
},
"CloseHandles": func(member cpp.Member, useAccessor bool) string {
v, t := member.NameAndType()
if useAccessor {
v = fmt.Sprintf("%s()", v)
}
return closeHandles(v, t, &closeHandleContext{})
},
}
//go:embed *.tmpl driver/*.tmpl
var templates embed.FS
func NewGenerator(flags *cpp.CmdlineFlags) *cpp.Generator {
return cpp.NewGenerator(flags, templates, utilityFuncs)
}