blob: 80e4f88bb397ec20bd1aa016a7eb3fc8eda44e35 [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 dart
import (
"fmt"
"strconv"
"strings"
gidlir "go.fuchsia.dev/fuchsia/tools/fidl/gidl/ir"
gidlmixer "go.fuchsia.dev/fuchsia/tools/fidl/gidl/mixer"
fidl "go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
)
func buildHandleDefs(defs []gidlir.HandleDef) string {
if len(defs) == 0 {
return ""
}
var builder strings.Builder
builder.WriteString("[\n")
for i, d := range defs {
switch d.Subtype {
case fidl.Channel:
builder.WriteString(fmt.Sprint("HandleSubtype.event,"))
case fidl.Event:
builder.WriteString(fmt.Sprint("HandleSubtype.channel,"))
default:
panic(fmt.Sprintf("unknown handle subtype: %v", d.Subtype))
}
// Write indices corresponding to the .gidl file handle_defs block.
builder.WriteString(fmt.Sprintf(" // #%d\n", i))
}
builder.WriteString("]")
return builder.String()
}
func buildHandleValues(handles []gidlir.Handle) string {
var builder strings.Builder
builder.WriteString("[\n")
for _, h := range handles {
builder.WriteString(fmt.Sprintf("%s,", buildHandleValue(h)))
}
builder.WriteString("]")
return builder.String()
}
func buildUnknownTableData(fields []gidlir.Field) string {
if len(fields) == 0 {
return "null"
}
var builder strings.Builder
builder.WriteString("{\n")
for _, field := range fields {
unknownData := field.Value.(gidlir.UnknownData)
builder.WriteString(fmt.Sprintf(
"%d: fidl.UnknownRawData(\n%s,\n%s\n),",
field.Key.UnknownOrdinal,
buildBytes(unknownData.Bytes),
buildHandleValues(unknownData.Handles)))
}
builder.WriteString("}")
return builder.String()
}
func visit(value interface{}, decl gidlmixer.Declaration) string {
switch value := value.(type) {
case bool:
return strconv.FormatBool(value)
case int64, uint64, float64:
switch decl := decl.(type) {
case *gidlmixer.IntegerDecl, *gidlmixer.FloatDecl:
return fmt.Sprintf("%#v", value)
case gidlmixer.NamedDeclaration:
return fmt.Sprintf("%s.ctor(%#v)", typeName(decl), value)
}
case gidlir.RawFloat:
switch decl.(*gidlmixer.FloatDecl).Subtype() {
case fidl.Float32:
return fmt.Sprintf("Uint32List.fromList([%#x]).buffer.asByteData().getFloat32(0, Endian.host)", value)
case fidl.Float64:
return fmt.Sprintf("Uint64List.fromList([%#x]).buffer.asByteData().getFloat64(0, Endian.host)", value)
}
case string:
return toDartStr(value)
case gidlir.Handle:
rawHandle := buildHandleValue(value)
handleDecl := decl.(*gidlmixer.HandleDecl)
switch handleDecl.Subtype() {
case fidl.Handle:
return rawHandle
case fidl.Channel:
return fmt.Sprintf("Channel(%s)", rawHandle)
case fidl.Event:
// Dart does not support events, so events are mapped to bare handles
return rawHandle
default:
panic(fmt.Sprintf("unknown handle subtype: %v", handleDecl.Subtype()))
}
case gidlir.Record:
switch decl := decl.(type) {
case *gidlmixer.StructDecl:
return onRecord(value, decl)
case *gidlmixer.TableDecl:
return onRecord(value, decl)
case *gidlmixer.UnionDecl:
return onUnion(value, decl)
}
case []interface{}:
switch decl := decl.(type) {
case *gidlmixer.ArrayDecl:
return onList(value, decl)
case *gidlmixer.VectorDecl:
return onList(value, decl)
}
case nil:
if !decl.IsNullable() {
panic(fmt.Sprintf("got nil for non-nullable type: %T", decl))
}
return "null"
}
panic(fmt.Sprintf("not implemented: %T", value))
}
func onRecord(value gidlir.Record, decl gidlmixer.RecordDeclaration) string {
var args []string
var unknownTableFields []gidlir.Field
for _, field := range value.Fields {
if field.Key.IsUnknown() {
unknownTableFields = append(unknownTableFields, field)
continue
}
fieldDecl, ok := decl.Field(field.Key.Name)
if !ok {
panic(fmt.Sprintf("field %s not found", field.Key.Name))
}
val := visit(field.Value, fieldDecl)
args = append(args, fmt.Sprintf("%s: %s", fidl.ToLowerCamelCase(field.Key.Name), val))
}
if len(unknownTableFields) > 0 {
args = append(args,
fmt.Sprintf("$unknownData: %s", buildUnknownTableData(unknownTableFields)))
}
return fmt.Sprintf("%s(%s)", fidl.ToUpperCamelCase(value.Name), strings.Join(args, ", "))
}
func onUnion(value gidlir.Record, decl *gidlmixer.UnionDecl) string {
for _, field := range value.Fields {
if field.Key.IsUnknown() {
unknownData := field.Value.(gidlir.UnknownData)
return fmt.Sprintf(
"%s.with$UnknownData(%d, fidl.UnknownRawData(%s, %s))",
value.Name,
field.Key.UnknownOrdinal,
buildBytes(unknownData.Bytes),
buildHandleValues(unknownData.Handles))
}
fieldDecl, ok := decl.Field(field.Key.Name)
if !ok {
panic(fmt.Sprintf("field %s not found", field.Key.Name))
}
val := visit(field.Value, fieldDecl)
return fmt.Sprintf("%s.with%s(%s)", value.Name, fidl.ToUpperCamelCase(field.Key.Name), val)
}
// Not currently possible to construct a union in dart with an invalid value.
panic("unions must have a value set")
}
func onList(value []interface{}, decl gidlmixer.ListDeclaration) string {
var elements []string
elemDecl := decl.Elem()
for _, item := range value {
elements = append(elements, visit(item, elemDecl))
}
if integerDecl, ok := elemDecl.(*gidlmixer.IntegerDecl); ok {
typeName := fidl.ToUpperCamelCase(string(integerDecl.Subtype()))
return fmt.Sprintf("%sList.fromList([%s])", typeName, strings.Join(elements, ", "))
}
if floatDecl, ok := elemDecl.(*gidlmixer.FloatDecl); ok {
typeName := fidl.ToUpperCamelCase(string(floatDecl.Subtype()))
return fmt.Sprintf("%sList.fromList([%s])", typeName, strings.Join(elements, ", "))
}
return fmt.Sprintf("[%s]", strings.Join(elements, ", "))
}
func typeName(decl gidlmixer.NamedDeclaration) string {
parts := strings.Split(decl.Name(), "/")
lastPart := parts[len(parts)-1]
return dartTypeName(lastPart)
}
func buildHandleValue(handle gidlir.Handle) string {
return fmt.Sprintf("handleDefs[%d]", handle)
}