blob: 352d09267e2c5bd94dc880c1d52c971c10a5f6b0 [file] [log] [blame]
// Copyright 2020 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 lib
import (
"fmt"
"strconv"
"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"
)
func BuildValueUnowned(value gidlir.Value, decl gidlmixer.Declaration, handleRepr HandleRepr) (string, string) {
var builder unownedBuilder
builder.handleRepr = handleRepr
valueVar := builder.visit(value, decl)
valueBuild := builder.String()
return valueBuild, valueVar
}
type unownedBuilder struct {
strings.Builder
varidx int
handleRepr HandleRepr
}
func (b *unownedBuilder) write(format string, vals ...interface{}) {
b.WriteString(fmt.Sprintf(format, vals...))
}
func (b *unownedBuilder) newVar() string {
b.varidx++
return fmt.Sprintf("v%d", b.varidx)
}
func primitiveTypeName(subtype fidlgen.PrimitiveSubtype) string {
switch subtype {
case fidlgen.Bool:
return "bool"
case fidlgen.Uint8, fidlgen.Uint16, fidlgen.Uint32, fidlgen.Uint64,
fidlgen.Int8, fidlgen.Int16, fidlgen.Int32, fidlgen.Int64:
return fmt.Sprintf("%s_t", subtype)
case fidlgen.Float32:
return "float"
case fidlgen.Float64:
return "double"
default:
panic(fmt.Sprintf("unexpected subtype %s", subtype))
}
}
func (b *unownedBuilder) visit(value gidlir.Value, decl gidlmixer.Declaration) string {
switch value := value.(type) {
case bool:
return fmt.Sprintf("%t", value)
case uint64:
return fmt.Sprintf("%s(%dll)", typeName(decl), value)
case int64:
if value == -9223372036854775808 {
return fmt.Sprintf("%s(-9223372036854775807ll - 1)", typeName(decl))
}
return fmt.Sprintf("%s(%dll)", typeName(decl), value)
case float64:
switch decl := decl.(type) {
case *gidlmixer.FloatDecl:
switch decl.Subtype() {
case fidlgen.Float32:
s := fmt.Sprintf("%g", value)
if strings.Contains(s, ".") {
return fmt.Sprintf("%sf", s)
}
return s
case fidlgen.Float64:
return fmt.Sprintf("%g", value)
}
}
case gidlir.RawFloat:
switch decl.(*gidlmixer.FloatDecl).Subtype() {
case fidlgen.Float32:
return fmt.Sprintf("([] { uint32_t u = %#b; float f; memcpy(&f, &u, sizeof(float)); return f; })()", value)
case fidlgen.Float64:
return fmt.Sprintf("([] { uint64_t u = %#b; double d; memcpy(&d, &u, sizeof(double)); return d; })()", value)
}
case string:
return fmt.Sprintf("fidl::StringView(%s)", strconv.Quote(value))
case gidlir.HandleWithRights:
if b.handleRepr == HandleReprDisposition || b.handleRepr == HandleReprInfo {
return fmt.Sprintf("%s(handle_defs[%d].handle)", typeName(decl), value.Handle)
}
return fmt.Sprintf("%s(handle_defs[%d])", typeName(decl), value.Handle)
case gidlir.Record:
switch decl := decl.(type) {
case *gidlmixer.StructDecl:
return b.visitStruct(value, decl)
case *gidlmixer.TableDecl:
return b.visitTable(value, decl)
case *gidlmixer.UnionDecl:
return b.visitUnion(value, decl)
}
case []gidlir.Value:
switch decl := decl.(type) {
case *gidlmixer.ArrayDecl:
return b.visitArray(value, decl)
case *gidlmixer.VectorDecl:
return b.visitVector(value, decl)
}
case nil:
return fmt.Sprintf("%s{}", typeName(decl))
}
panic(fmt.Sprintf("not implemented: %T", value))
}
func (b *unownedBuilder) visitStruct(value gidlir.Record, decl *gidlmixer.StructDecl) string {
containerVar := b.newVar()
b.write(
"%s %s{};\n", declName(decl), containerVar)
for _, field := range value.Fields {
fieldDecl, ok := decl.Field(field.Key.Name)
if !ok {
panic(fmt.Sprintf("field %s not found", field.Key.Name))
}
stringBeforeVisitField := b.String()
fieldValue := b.visit(field.Value, fieldDecl)
// if visiting the field does not write any data to the string builder
// then its return value is a temporary object and so cannot be moved
// (which will prevent copy elision)
if stringBeforeVisitField == b.String() {
b.write("%s.%s = %s;\n", containerVar, field.Key.Name, fieldValue)
} else {
b.write("%s.%s = std::move(%s);\n", containerVar, field.Key.Name, fieldValue)
}
}
var result string
if decl.IsNullable() {
viewVar := b.newVar()
b.write("auto %s = fidl::ObjectView<%s>::FromExternal(&%s);\n", viewVar, typeNameIgnoreNullable(decl), containerVar)
result = viewVar
} else {
result = containerVar
}
return fmt.Sprintf("std::move(%s)", result)
}
func (b *unownedBuilder) visitTable(value gidlir.Record, decl *gidlmixer.TableDecl) string {
frameVar := b.newVar()
b.write(
"fidl::WireTableFrame<%s> %s;\n", declName(decl), frameVar)
tableVar := b.newVar()
b.write(
"%s %s(::fidl::ObjectView<::fidl::WireTableFrame<%s>>::FromExternal(&%s));\n", declName(decl), tableVar, declName(decl), frameVar)
for _, field := range value.Fields {
if field.Key.IsUnknown() {
panic("LLCPP does not support constructing unknown fields")
}
fieldDecl, ok := decl.Field(field.Key.Name)
if !ok {
panic(fmt.Sprintf("field %s not found", field.Key.Name))
}
fieldVar := b.visit(field.Value, fieldDecl)
storageVar := b.newVar()
b.write("auto %s = std::move(%s);\n", storageVar, fieldVar)
if fieldDecl.IsInlinableInEnvelope() {
b.write(
"%s.set_%s(%s);\n", tableVar, field.Key.Name, storageVar)
} else {
b.write(
"%s.set_%s(fidl::ObjectView<%s>::FromExternal(&%s));\n", tableVar, field.Key.Name, typeName(fieldDecl), storageVar)
}
}
return fmt.Sprintf("std::move(%s)", tableVar)
}
func (b *unownedBuilder) visitUnion(value gidlir.Record, decl *gidlmixer.UnionDecl) string {
containerVar := b.newVar()
b.write(
"%s %s;\n", declName(decl), containerVar)
for _, field := range value.Fields {
if field.Key.IsUnknown() {
panic("LLCPP does not support constructing unknown fields")
}
fieldDecl, ok := decl.Field(field.Key.Name)
if !ok {
panic(fmt.Sprintf("field %s not found", field.Key.Name))
}
fieldVar := b.visit(field.Value, fieldDecl)
storageVar := b.newVar()
b.write("auto %s = std::move(%s);\n", storageVar, fieldVar)
if fieldDecl.IsInlinableInEnvelope() {
b.write(
"%s.set_%s(%s);\n", containerVar, field.Key.Name, storageVar)
} else {
b.write(
"%s.set_%s(fidl::ObjectView<%s>::FromExternal(&%s));\n", containerVar, field.Key.Name, typeName(fieldDecl), storageVar)
}
}
return fmt.Sprintf("std::move(%s)", containerVar)
}
func (b *unownedBuilder) buildListItems(value []gidlir.Value, decl gidlmixer.ListDeclaration) []string {
var elements []string
elemDecl := decl.Elem()
for _, item := range value {
elements = append(elements, fmt.Sprintf("%s", b.visit(item, elemDecl)))
}
return elements
}
func (b *unownedBuilder) visitArray(value []gidlir.Value, decl *gidlmixer.ArrayDecl) string {
elements := b.buildListItems(value, decl)
sliceVar := b.newVar()
b.write("FIDL_ALIGNDECL auto %s = %s{%s};\n",
sliceVar, typeName(decl), strings.Join(elements, ", "))
return sliceVar
}
func (b *unownedBuilder) visitVector(value []gidlir.Value, decl *gidlmixer.VectorDecl) string {
if len(value) == 0 {
sliceVar := b.newVar()
b.write("auto %s = %s();\n",
sliceVar, typeName(decl))
return sliceVar
}
elements := b.buildListItems(value, decl)
arrayVar := b.newVar()
b.write("auto %s = fidl::Array<%s, %d>{%s};\n",
arrayVar, typeName(decl.Elem()), len(elements), strings.Join(elements, ", "))
sliceVar := b.newVar()
b.write("auto %s = fidl::VectorView<%s>::FromExternal(%s.data(), %d);\n", sliceVar,
typeName(decl.Elem()), arrayVar, len(elements))
return fmt.Sprintf("std::move(%s)", sliceVar)
}