blob: efafb44ec5ee9e596de941c2477ddab28cffa20b [file] [log] [blame] [edit]
// 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 llcpp
import (
"fmt"
"strconv"
"strings"
"go.fuchsia.dev/fuchsia/tools/fidl/gidl/lib/ir"
"go.fuchsia.dev/fuchsia/tools/fidl/gidl/lib/mixer"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
)
func BuildValueUnowned(value ir.Value, decl mixer.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) adoptHandle(decl mixer.Declaration, value ir.Handle) string {
if b.handleRepr == HandleReprDisposition || b.handleRepr == HandleReprInfo {
return fmt.Sprintf("%s(handle_defs[%d].handle)", typeName(decl), value)
}
return fmt.Sprintf("%s(handle_defs[%d])", typeName(decl), value)
}
func (b *unownedBuilder) visit(value ir.Value, decl mixer.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 *mixer.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 ir.RawFloat:
switch decl.(*mixer.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 ir.Handle:
switch decl := decl.(type) {
case *mixer.HandleDecl:
return b.adoptHandle(decl, value)
case *mixer.ClientEndDecl:
return fmt.Sprintf("%s(%s)", typeName(decl), b.adoptHandle(decl.UnderlyingHandleDecl(), value))
case *mixer.ServerEndDecl:
return fmt.Sprintf("%s(%s)", typeName(decl), b.adoptHandle(decl.UnderlyingHandleDecl(), value))
}
case ir.Record:
switch decl := decl.(type) {
case *mixer.StructDecl:
return b.visitStruct(value, decl)
case *mixer.TableDecl:
return b.visitTable(value, decl)
case *mixer.UnionDecl:
return b.visitUnion(value, decl)
}
case []ir.Value:
switch decl := decl.(type) {
case *mixer.ArrayDecl:
return b.visitArray(value, decl)
case *mixer.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 ir.Record, decl *mixer.StructDecl) string {
containerVar := b.newVar()
b.write(
"%s %s{};\n", declName(decl), containerVar)
for _, field := range value.Fields {
stringBeforeVisitField := b.String()
fieldValue := b.visit(field.Value, decl.Field(field.Key.Name))
// 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 ir.Record, decl *mixer.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 := decl.Field(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 ir.Record, decl *mixer.UnionDecl) string {
containerVar := b.newVar()
for _, field := range value.Fields {
if field.Key.IsUnknown() {
panic("LLCPP does not support constructing unknown fields")
}
fieldDecl := decl.Field(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 %s = %s::With%s(%s);\n",
declName(decl), containerVar, declName(decl), fidlgen.ToUpperCamelCase(field.Key.Name), storageVar)
} else {
b.write("%s %s = %s::With%s(fidl::ObjectView<%s>::FromExternal(&%s));\n",
declName(decl), containerVar, declName(decl), fidlgen.ToUpperCamelCase(field.Key.Name), typeName(fieldDecl), storageVar)
}
}
return fmt.Sprintf("std::move(%s)", containerVar)
}
func (b *unownedBuilder) buildListItems(value []ir.Value, decl mixer.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 []ir.Value, decl *mixer.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 []ir.Value, decl *mixer.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)
}