blob: 53683ae877632dca23c97501a1aeb55fa3ef6eaa [file] [log] [blame]
// Copyright 2021 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 rust
import (
"fmt"
"strings"
gidlir "go.fuchsia.dev/fuchsia/tools/fidl/gidl/ir"
gidlmixer "go.fuchsia.dev/fuchsia/tools/fidl/gidl/mixer"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
)
// Returns Rust code that resets all handles in expr (an expression of type
// "&mut _") to the invalid handle without closing the original handles.
func buildForgetHandles(expr string, value gidlir.Value, decl gidlmixer.Declaration) string {
var b forgetHandleBuilder
b.visit(expr, value, decl)
return strings.TrimSpace(b.String())
}
type forgetHandleBuilder struct {
strings.Builder
}
func (b *forgetHandleBuilder) write(format string, args ...interface{}) {
b.WriteString(fmt.Sprintf(format, args...))
}
func (b *forgetHandleBuilder) visit(expr string, value gidlir.Value, decl gidlmixer.Declaration) {
switch value := value.(type) {
case gidlir.Handle, gidlir.HandleWithRights:
b.write("std::mem::forget(std::mem::replace(%s, Handle::invalid().into()));\n", expr)
case gidlir.Record:
decl := decl.(gidlmixer.RecordDeclaration)
switch decl.(type) {
case *gidlmixer.StructDecl:
for _, field := range value.Fields {
fieldDecl, ok := decl.Field(field.Key.Name)
if !ok {
panic(fmt.Sprintf("field %s not found", field.Key.Name))
}
b.visit(fmt.Sprintf("(&mut %s.%s)", expr, field.Key.Name), field.Value, fieldDecl)
}
case *gidlmixer.TableDecl:
hasUnknown := false
for _, field := range value.Fields {
if field.Key.IsUnknown() {
hasUnknown = true
continue
}
fieldDecl, ok := decl.Field(field.Key.Name)
if !ok {
panic(fmt.Sprintf("field %s not found", field.Key.Name))
}
b.visit(fmt.Sprintf("%s.%s.as_mut().unwrap()", expr, field.Key.Name), field.Value, fieldDecl)
}
if decl.IsResourceType() && hasUnknown {
b.write(`for data in %s.unknown_data.as_mut().unwrap().values_mut() {
for h in data.handles.drain(..) { std::mem::forget(h); }
}
`, expr)
}
case *gidlmixer.UnionDecl:
if len(value.Fields) != 1 {
panic(fmt.Sprintf("union has %d fields, expected 1", len(value.Fields)))
}
field := value.Fields[0]
if field.Key.IsKnown() {
fieldDecl, ok := decl.Field(field.Key.Name)
fieldName := fidlgen.ToUpperCamelCase(field.Key.Name)
if !ok {
panic(fmt.Sprintf("field %s not found", field.Key.Name))
}
// Use another builder so that we only emit the match statement
// if there are any handles within to forget.
var inner forgetHandleBuilder
inner.visit("x", field, fieldDecl)
if inner.Len() == 0 {
break
}
b.write(`match %s {
%s::%s(x) => {
%s
}
_ => unreachable!(),
}
`, expr, declName(decl), fieldName, inner.String())
} else {
unknownData := field.Value.(gidlir.UnknownData)
if len(unknownData.Handles) != 0 {
if !decl.IsResourceType() {
panic("non-resource type should not have unknown handles")
}
b.write(`match %s {
#[allow(deprecated)]
%s::__Unknown { data, .. } => {
for h in data.handles.drain(..) { std::mem::forget(h); }
}
_ => unreachable!(),
}
`, expr, declName(decl))
}
}
}
case []gidlir.Value:
elemDecl := decl.(gidlmixer.ListDeclaration).Elem()
for i, elem := range value {
b.visit(fmt.Sprintf("(&mut %s[%d])", expr, i), elem, elemDecl)
}
}
}