| // 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 hlcpp |
| |
| import ( |
| "bytes" |
| "encoding/hex" |
| "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 escapeStr(value string) string { |
| if fidlgen.PrintableASCII(value) { |
| return strconv.Quote(value) |
| } |
| var ( |
| buf bytes.Buffer |
| src = []byte(value) |
| dstLen = hex.EncodedLen(len(src)) |
| dst = make([]byte, dstLen) |
| ) |
| hex.Encode(dst, src) |
| buf.WriteRune('"') |
| for i := 0; i < dstLen; i += 2 { |
| buf.WriteString("\\x") |
| buf.WriteByte(dst[i]) |
| buf.WriteByte(dst[i+1]) |
| } |
| buf.WriteRune('"') |
| return buf.String() |
| } |
| |
| func buildHandleDef(def gidlir.HandleDef) string { |
| switch def.Subtype { |
| case fidlgen.Channel: |
| return fmt.Sprintf("fidl::test::util::CreateChannel(%d)", def.Rights) |
| case fidlgen.Event: |
| return fmt.Sprintf("fidl::test::util::CreateEvent(%d)", def.Rights) |
| default: |
| panic(fmt.Sprintf("unsupported handle subtype: %s", def.Subtype)) |
| } |
| } |
| |
| func handleType(subtype fidlgen.HandleSubtype) string { |
| switch subtype { |
| case fidlgen.Channel: |
| return "ZX_OBJ_TYPE_CHANNEL" |
| case fidlgen.Event: |
| return "ZX_OBJ_TYPE_EVENT" |
| default: |
| panic(fmt.Sprintf("unsupported handle subtype: %s", subtype)) |
| } |
| } |
| |
| func BuildHandleDefs(defs []gidlir.HandleDef) string { |
| if len(defs) == 0 { |
| return "" |
| } |
| var builder strings.Builder |
| builder.WriteString("std::vector<zx_handle_t>{\n") |
| for i, d := range defs { |
| builder.WriteString(buildHandleDef(d)) |
| // Write indices corresponding to the .gidl file handle_defs block. |
| builder.WriteString(fmt.Sprintf(", // #%d\n", i)) |
| } |
| builder.WriteString("}") |
| return builder.String() |
| } |
| |
| func BuildHandleInfoDefs(defs []gidlir.HandleDef) string { |
| if len(defs) == 0 { |
| return "" |
| } |
| var builder strings.Builder |
| builder.WriteString("std::vector<zx_handle_info_t>{\n") |
| for i, d := range defs { |
| builder.WriteString(fmt.Sprintf(` |
| // #%d |
| zx_handle_info_t{ |
| .handle = %s, |
| .type = %s, |
| .rights = %d, |
| .unused = 0u, |
| }, |
| `, i, buildHandleDef(d), handleType(d.Subtype), d.Rights)) |
| } |
| builder.WriteString("}") |
| return builder.String() |
| } |
| |
| func newCppValueBuilder() cppValueBuilder { |
| return cppValueBuilder{} |
| } |
| |
| type cppValueBuilder struct { |
| strings.Builder |
| |
| varidx int |
| handleExtractOp string |
| } |
| |
| func (b *cppValueBuilder) newVar() string { |
| b.varidx++ |
| return fmt.Sprintf("v%d", b.varidx) |
| } |
| |
| func (b *cppValueBuilder) visit(value interface{}, decl gidlmixer.Declaration) string { |
| switch value := value.(type) { |
| case bool: |
| return fmt.Sprintf("%t", value) |
| case int64: |
| intString := fmt.Sprintf("%dll", value) |
| if value == -9223372036854775808 { |
| intString = "-9223372036854775807ll - 1" |
| } |
| switch decl := decl.(type) { |
| case *gidlmixer.IntegerDecl: |
| return intString |
| case *gidlmixer.BitsDecl: |
| return fmt.Sprintf("%s(%s)", typeName(decl), intString) |
| case *gidlmixer.EnumDecl: |
| return fmt.Sprintf("%s(%s)", typeName(decl), intString) |
| } |
| case uint64: |
| switch decl := decl.(type) { |
| case *gidlmixer.IntegerDecl: |
| return fmt.Sprintf("%dull", value) |
| case *gidlmixer.BitsDecl: |
| return fmt.Sprintf("%s(%dull)", typeName(decl), value) |
| case *gidlmixer.EnumDecl: |
| return fmt.Sprintf("%s(%dull)", 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) |
| } else { |
| 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, 4); return f; })()", value) |
| case fidlgen.Float64: |
| return fmt.Sprintf("([] { uint64_t u = %#b; double d; memcpy(&d, &u, 8); return d; })()", value) |
| } |
| case string: |
| return fmt.Sprintf("%s(%s, %d)", typeName(decl), escapeStr(value), len(value)) |
| case gidlir.HandleWithRights: |
| return fmt.Sprintf("%s(handle_defs[%d]%s)", typeName(decl), value.Handle, b.handleExtractOp) |
| case gidlir.Record: |
| return b.visitRecord(value, decl.(gidlmixer.RecordDeclaration)) |
| case []interface{}: |
| 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 *cppValueBuilder) visitRecord(value gidlir.Record, decl gidlmixer.RecordDeclaration) string { |
| containerVar := b.newVar() |
| nullable := decl.IsNullable() |
| if nullable { |
| b.Builder.WriteString(fmt.Sprintf( |
| "%s %s = std::make_unique<%s>();\n", typeName(decl), containerVar, declName(decl))) |
| } else { |
| b.Builder.WriteString(fmt.Sprintf("%s %s;\n", typeName(decl), containerVar)) |
| } |
| |
| _, isTable := decl.(*gidlmixer.TableDecl) |
| for _, field := range value.Fields { |
| accessor := "." |
| if nullable { |
| accessor = "->" |
| } |
| b.Builder.WriteString("\n") |
| |
| if field.Key.IsUnknown() { |
| if isTable { |
| unknownData := field.Value.(gidlir.UnknownData) |
| if decl.IsResourceType() { |
| b.Builder.WriteString(fmt.Sprintf(`::fidl::UnknownData _data = { |
| .bytes=%s, |
| .handles=%s |
| }; |
| %s%sSetUnknownDataEntry(%dlu, std::move(_data)); |
| `, BuildBytes(unknownData.Bytes), buildHandles(unknownData.Handles, b.handleExtractOp), containerVar, accessor, field.Key.UnknownOrdinal)) |
| } else { |
| b.Builder.WriteString(fmt.Sprintf( |
| "%s%sSetUnknownDataEntry(%dlu, %s);\n", |
| containerVar, accessor, field.Key.UnknownOrdinal, BuildBytes(unknownData.Bytes))) |
| } |
| } else { |
| unknownData := field.Value.(gidlir.UnknownData) |
| if decl.IsResourceType() { |
| b.Builder.WriteString(fmt.Sprintf( |
| "%s%sSetUnknownData(static_cast<fidl_xunion_tag_t>(%dlu), %s, %s);\n", |
| containerVar, accessor, field.Key.UnknownOrdinal, BuildBytes(unknownData.Bytes), |
| buildHandles(unknownData.Handles, b.handleExtractOp))) |
| } else { |
| b.Builder.WriteString(fmt.Sprintf( |
| "%s%sSetUnknownData(static_cast<fidl_xunion_tag_t>(%dlu), %s);\n", |
| containerVar, accessor, field.Key.UnknownOrdinal, BuildBytes(unknownData.Bytes))) |
| } |
| } |
| continue |
| } |
| |
| 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) |
| |
| switch decl.(type) { |
| case *gidlmixer.StructDecl: |
| b.Builder.WriteString(fmt.Sprintf( |
| "%s%s%s = %s;\n", containerVar, accessor, field.Key.Name, fieldVar)) |
| default: |
| b.Builder.WriteString(fmt.Sprintf( |
| "%s%sset_%s(%s);\n", containerVar, accessor, field.Key.Name, fieldVar)) |
| } |
| } |
| return fmt.Sprintf("std::move(%s)", containerVar) |
| } |
| |
| func (b *cppValueBuilder) visitArray(value []interface{}, decl *gidlmixer.ArrayDecl) string { |
| var elements []string |
| elemDecl := decl.Elem() |
| for _, item := range value { |
| elements = append(elements, fmt.Sprintf("%s", b.visit(item, elemDecl))) |
| } |
| // Populate the array using aggregate initialization. |
| return fmt.Sprintf("%s{%s}", |
| typeName(decl), strings.Join(elements, ", ")) |
| } |
| |
| func (b *cppValueBuilder) visitVector(value []interface{}, decl *gidlmixer.VectorDecl) string { |
| elemDecl := decl.Elem() |
| var elements []string |
| for _, item := range value { |
| elements = append(elements, b.visit(item, elemDecl)) |
| } |
| if _, ok := elemDecl.(gidlmixer.PrimitiveDeclaration); ok { |
| // Populate the vector using aggregate initialization. |
| return fmt.Sprintf("%s{%s}", |
| typeName(decl), strings.Join(elements, ", ")) |
| } |
| vectorVar := b.newVar() |
| // Populate the vector using push_back. We can't use an initializer list |
| // because they always copy, which breaks if the element is a unique_ptr. |
| b.Builder.WriteString(fmt.Sprintf("%s %s;\n", typeName(decl), vectorVar)) |
| for _, element := range elements { |
| b.Builder.WriteString(fmt.Sprintf("%s.push_back(%s);\n", vectorVar, element)) |
| } |
| return fmt.Sprintf("std::move(%s)", vectorVar) |
| } |
| |
| func typeName(decl gidlmixer.Declaration) string { |
| switch decl := decl.(type) { |
| case gidlmixer.PrimitiveDeclaration: |
| return primitiveTypeName(decl.Subtype()) |
| case gidlmixer.NamedDeclaration: |
| if decl.IsNullable() { |
| return fmt.Sprintf("std::unique_ptr<%s>", declName(decl)) |
| } |
| return declName(decl) |
| case *gidlmixer.StringDecl: |
| if decl.IsNullable() { |
| return "::fidl::StringPtr" |
| } |
| return "std::string" |
| case *gidlmixer.ArrayDecl: |
| return fmt.Sprintf("std::array<%s, %d>", typeName(decl.Elem()), decl.Size()) |
| case *gidlmixer.VectorDecl: |
| if decl.IsNullable() { |
| return fmt.Sprintf("::fidl::VectorPtr<%s>", typeName(decl.Elem())) |
| } |
| return fmt.Sprintf("std::vector<%s>", typeName(decl.Elem())) |
| case *gidlmixer.HandleDecl: |
| switch decl.Subtype() { |
| case fidlgen.Handle: |
| return "zx::handle" |
| case fidlgen.Channel: |
| return "zx::channel" |
| case fidlgen.Event: |
| return "zx::event" |
| default: |
| panic(fmt.Sprintf("Handle subtype not supported %s", decl.Subtype())) |
| } |
| default: |
| panic("unhandled case") |
| } |
| } |
| |
| func declName(decl gidlmixer.NamedDeclaration) string { |
| parts := strings.Split(decl.Name(), "/") |
| return strings.Join(parts, "::") |
| } |
| |
| 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 BuildBytes(bytes []byte) string { |
| var builder strings.Builder |
| builder.WriteString("std::vector<uint8_t>{") |
| for i, b := range bytes { |
| if i%8 == 0 { |
| builder.WriteString("\n") |
| } |
| builder.WriteString(fmt.Sprintf("0x%02x,", b)) |
| } |
| builder.WriteString("\n}") |
| return builder.String() |
| } |
| |
| func buildRawHandleImpl(handles []gidlir.Handle, handleType, handleExtractOp string) string { |
| if len(handles) == 0 { |
| return fmt.Sprintf("std::vector<%s>{}", handleType) |
| } |
| var builder strings.Builder |
| builder.WriteString(fmt.Sprintf("std::vector<%s>{\n", handleType)) |
| for i, h := range handles { |
| builder.WriteString(fmt.Sprintf("handle_defs[%d]%s,", h, handleExtractOp)) |
| if i%8 == 7 { |
| builder.WriteString("\n") |
| } |
| } |
| builder.WriteString("}") |
| return builder.String() |
| } |
| |
| func BuildRawHandles(handles []gidlir.Handle) string { |
| return buildRawHandleImpl(handles, "zx_handle_t", "") |
| } |
| |
| func BuildRawHandlesFromHandleInfos(handles []gidlir.Handle) string { |
| return buildRawHandleImpl(handles, "zx_handle_t", ".handle") |
| } |
| |
| func BuildRawHandleInfos(handles []gidlir.Handle) string { |
| return buildRawHandleImpl(handles, "zx_handle_info_t", "") |
| } |
| |
| func BuildRawHandleDispositions(handle_dispositions []gidlir.HandleDisposition) string { |
| if len(handle_dispositions) == 0 { |
| return fmt.Sprintf("std::vector<zx_handle_disposition_t>{}") |
| } |
| var builder strings.Builder |
| builder.WriteString(fmt.Sprintf("std::vector<zx_handle_disposition_t>{")) |
| for _, h := range handle_dispositions { |
| builder.WriteString(fmt.Sprintf(` |
| { |
| .operation = ZX_HANDLE_OP_MOVE, |
| .handle = handle_defs[%d], |
| .type = %d, |
| .rights = %d, |
| .result = ZX_OK, |
| },`, h.Handle, h.Type, h.Rights)) |
| } |
| builder.WriteString("}") |
| return builder.String() |
| } |
| |
| func buildHandles(handles []gidlir.Handle, handleExtractOp string) string { |
| if len(handles) == 0 { |
| return "std::vector<zx::handle>{}" |
| } |
| var builder strings.Builder |
| // Initializer-list vectors only work for copyable types. zx::handle has no |
| // copy constructor, so we use an immediately-invoked lambda instead. |
| builder.WriteString("([&handle_defs] {\n") |
| builder.WriteString("std::vector<zx::handle> v;\n") |
| for _, h := range handles { |
| builder.WriteString(fmt.Sprintf("v.emplace_back(handle_defs[%d]%s);\n", h, handleExtractOp)) |
| } |
| builder.WriteString("return v;\n") |
| builder.WriteString("})()") |
| return builder.String() |
| } |