| // 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 fidlgen_cpp |
| |
| import ( |
| "fmt" |
| "strings" |
| |
| "go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen" |
| ) |
| |
| var zxNs namespace = newNamespace("zx") |
| var fidlNs namespace = newNamespace("fidl") |
| var internalNs namespace = fidlNs.append("internal") |
| |
| // variant controls how we refer to domain object declarations. |
| type variant string |
| |
| const ( |
| noVariant variant = "" |
| hlcppVariant variant = "hlcpp" |
| unifiedVariant variant = "unified" |
| wireVariant variant = "wire" |
| |
| // for use in testing: |
| testingVariant variant = "testing" |
| ) |
| |
| var currentVariant = noVariant |
| |
| // Namespace represents a C++ namespace. |
| type namespace []string |
| |
| func newNamespace(ns string) namespace { |
| return namespace(strings.Split(ns, "::")) |
| } |
| |
| // Namespace is implemented to satisfy the namespaced interface. |
| func (ns namespace) Namespace() namespace { |
| return ns |
| } |
| |
| // String returns the fully qualified namespace including leading ::. |
| func (ns namespace) String() string { |
| if len(ns) == 0 { |
| return "" |
| } |
| return "::" + ns.NoLeading() |
| } |
| |
| // NoLeading returns the fully qualified namespace without the leading ::. |
| func (ns namespace) NoLeading() string { |
| return strings.Join(ns, "::") |
| } |
| |
| // append returns a new namespace with an additional component. |
| func (ns namespace) append(part string) namespace { |
| newNs := make([]string, len(ns)+1) |
| copy(newNs, ns) |
| newNs[len(ns)] = part |
| return namespace(newNs) |
| } |
| |
| // member creates a named declaration within the namespace |
| func (ns namespace) member(n string) name { |
| return name{name: stringNamePart(n), ns: ns} |
| } |
| |
| // nameVariants is the name of a type or a template used in the various C++ bindings. |
| // |
| // Names are more general than FIDL declarations. All declarations have a corresponding |
| // type name, but some types are not declared in the generated code (e.g. zx::vmo), |
| // or are non-nominal (e.g. std::vector<FooBarDecl>). |
| type nameVariants struct { |
| HLCPP name |
| |
| // Unified is like HLCPP, except it consists of type aliases, declared in |
| // the unified bindings, to natural types. For example, the HLCPP name |
| // |
| // fuchsia::my::lib::FooStruct |
| // |
| // would be aliased to the Unified name |
| // |
| // fuchsia_my_lib::FooStruct. |
| // |
| // Similarly, if HLCPP is |
| // |
| // std::array<std::unique_ptr<fuchsia::my::lib::FooStruct>, 5> |
| // |
| // then Unified would use the alias in the template argument: |
| // |
| // std::array<std::unique_ptr<fuchsia_my_lib::FooStruct>, 5>. |
| // |
| // In case of client and server protocol endpoints, there is no alias, |
| // and Unified is the same as HLCPP. |
| Unified name |
| |
| Wire name |
| } |
| |
| // commonNameVariants returns a nameVariants with the same Name for both Wire and HLCPP variants. |
| func commonNameVariants(decl name) nameVariants { |
| return nameVariants{ |
| HLCPP: decl, |
| Unified: decl, |
| Wire: decl, |
| } |
| } |
| |
| func (dn nameVariants) String() string { |
| switch currentVariant { |
| case noVariant: |
| fidlgen.TemplateFatalf("Called nameVariants.String() on %s/%s when currentVariant isn't set.\n", |
| dn.HLCPP, dn.Wire) |
| case hlcppVariant: |
| return dn.HLCPP.String() |
| case unifiedVariant: |
| return dn.Unified.String() |
| case wireVariant: |
| return dn.Wire.String() |
| case testingVariant: |
| return fmt.Sprintf("%#v", dn) |
| } |
| panic("not reached") |
| } |
| |
| func (dn nameVariants) Name() string { |
| switch currentVariant { |
| case noVariant: |
| fidlgen.TemplateFatalf("Called nameVariants.Name() on %s/%s when currentVariant isn't set.\n", |
| dn.HLCPP, dn.Wire) |
| case hlcppVariant: |
| return dn.HLCPP.Name() |
| case unifiedVariant: |
| return dn.Unified.Name() |
| case wireVariant: |
| return dn.Wire.Name() |
| } |
| panic("not reached") |
| } |
| |
| func (dn nameVariants) Self() string { |
| switch currentVariant { |
| case noVariant: |
| fidlgen.TemplateFatalf("Called nameVariants.Self() on %s/%s when currentVariant isn't set.\n", dn.HLCPP, dn.Wire) |
| case hlcppVariant: |
| return dn.HLCPP.Self() |
| case unifiedVariant: |
| return dn.Unified.Self() |
| case wireVariant: |
| return dn.Wire.Self() |
| } |
| panic("not reached") |
| } |
| |
| func (dn nameVariants) NoLeading() string { |
| switch currentVariant { |
| case noVariant: |
| fidlgen.TemplateFatalf("Called nameVariants.NoLeading() on %s/%s when currentVariant isn't set.\n", dn.HLCPP, dn.Wire) |
| case hlcppVariant: |
| return dn.HLCPP.NoLeading() |
| case unifiedVariant: |
| return dn.Unified.NoLeading() |
| case wireVariant: |
| return dn.Wire.NoLeading() |
| } |
| panic("not reached") |
| } |
| |
| func (dn nameVariants) Namespace() namespace { |
| switch currentVariant { |
| case noVariant: |
| fidlgen.TemplateFatalf("Called nameVariants.Namespace() on %s/%s when currentVariant isn't set.\n", |
| dn.HLCPP, dn.Wire) |
| case hlcppVariant: |
| return dn.HLCPP.Namespace() |
| case unifiedVariant: |
| return dn.Unified.Namespace() |
| case wireVariant: |
| return dn.Wire.Namespace() |
| } |
| panic("not reached") |
| } |
| |
| // appendName returns a new nameVariants with an suffix appended to the name portions. |
| func (dn nameVariants) appendName(suffix string) nameVariants { |
| return nameVariants{ |
| HLCPP: dn.HLCPP.appendName(suffix), |
| Unified: dn.Unified.appendName(suffix), |
| Wire: dn.Wire.appendName(suffix), |
| } |
| } |
| |
| // prependName returns a new nameVariants with an prefix prepended to the name portions. |
| func (dn nameVariants) prependName(prefix string) nameVariants { |
| return nameVariants{ |
| HLCPP: dn.HLCPP.prependName(prefix), |
| Unified: dn.Unified.prependName(prefix), |
| Wire: dn.Wire.prependName(prefix), |
| } |
| } |
| |
| // appendNamespace returns a new nameVariants with additional C++ namespace components appended. |
| func (dn nameVariants) appendNamespace(c string) nameVariants { |
| return nameVariants{ |
| HLCPP: dn.HLCPP.appendNamespace(c), |
| Unified: dn.Unified.appendNamespace(c), |
| Wire: dn.Wire.appendNamespace(c), |
| } |
| } |
| |
| // nest returns a new name for a class nested inside the existing name. |
| func (dn nameVariants) nest(c string) nameVariants { |
| return nameVariants{ |
| HLCPP: dn.HLCPP.nest(c), |
| Unified: dn.Unified.nest(c), |
| Wire: dn.Wire.nest(c), |
| } |
| } |
| |
| // nestVariants returns a new name for a class nested inside the existing name. |
| func (dn nameVariants) nestVariants(v nameVariants) nameVariants { |
| if len(v.HLCPP.Namespace()) != 0 { |
| panic(fmt.Sprintf("Can't nest a name with a namespace: %v", v.HLCPP.String())) |
| } |
| if len(v.Unified.Namespace()) != 0 { |
| panic(fmt.Sprintf("Can't nest a name with a namespace: %v", v.Unified.String())) |
| } |
| if len(v.Wire.Namespace()) != 0 { |
| panic(fmt.Sprintf("Can't nest a name with a namespace: %v", v.Wire.String())) |
| } |
| return nameVariants{ |
| HLCPP: dn.HLCPP.nest(v.HLCPP.Name()), |
| Unified: dn.Unified.nest(v.Unified.Name()), |
| Wire: dn.Wire.nest(v.Wire.Name()), |
| } |
| } |
| |
| // nameVariantsForHandle returns the C++ name for a handle type |
| func nameVariantsForHandle(t fidlgen.HandleSubtype) nameVariants { |
| return commonNameVariants(zxNs.member(string(t))) |
| } |
| |
| // primitiveNameVariants returns a nameVariants for a primitive type, common across all bindings. |
| func primitiveNameVariants(primitive string) nameVariants { |
| return commonNameVariants(makeName(primitive)) |
| } |
| |
| // namePart represents part of non-namespace part of a name. |
| // It's implemented by three types: stringNamePart, nestedNamePart and templateNamePart. |
| // These form a tree to hold the structure of a name so that it can be accessed and manipulated safely. |
| // For example in fidl::WireServer<fuchsia_library::Protocol>::ProtocolMethod this would be: |
| // WireServer<fuchsia_library::Protocol>::ProtocolMethod |
| // |-nestedNP---------------------------------------------| |
| // |-templateNP---------------------------| |-stringNP---| |
| // |-stringNP---|-Name--------------------| |
| // TODO(ianloic): rename to idPart |
| type namePart interface { |
| // String returns the full name. |
| String() string |
| // Self returns how the type refers to itself, like in constructor & destructor names. |
| // For a nested name like "Foo::Bar::Baz" this would be "Baz". |
| // For a template name like "Foo::Bar<Baz>" this would be "Bar". |
| Self() string |
| |
| // nest returns a new name for a class nested inside the existing name. |
| nest(name string) namePart |
| // Template returns a new name with this name being an template applied to the |args|. |
| template(args string) namePart |
| // prependName returns a new name with a prefix prepended. |
| prependName(prefix string) namePart |
| // appendName returns a new name with a suffix appended. |
| appendName(suffix string) namePart |
| } |
| |
| type stringNamePart string |
| |
| var _ namePart = (*stringNamePart)(nil) |
| |
| func (n stringNamePart) String() string { |
| return string(n) |
| } |
| |
| func (n stringNamePart) Self() string { |
| return string(n) |
| } |
| |
| func (n stringNamePart) nest(name string) namePart { |
| return newNestedNamePart(n, stringNamePart(name)) |
| } |
| |
| func (n stringNamePart) template(args string) namePart { |
| return newTemplateNamePart(n, args) |
| } |
| |
| func (n stringNamePart) prependName(prefix string) namePart { |
| return stringNamePart(prefix + string(n)) |
| } |
| |
| func (n stringNamePart) appendName(suffix string) namePart { |
| return stringNamePart(string(n) + suffix) |
| } |
| |
| type nestedNamePart struct { |
| left namePart |
| right namePart |
| } |
| |
| var _ namePart = (*nestedNamePart)(nil) |
| |
| func newNestedNamePart(left, right namePart) namePart { |
| return nestedNamePart{left, right} |
| } |
| |
| func (n nestedNamePart) String() string { |
| return fmt.Sprintf("%s::%s", n.left, n.right) |
| } |
| |
| func (n nestedNamePart) Self() string { |
| return n.right.Self() |
| } |
| |
| func (n nestedNamePart) nest(name string) namePart { |
| return nestedNamePart{n.left, n.right.nest(name)} |
| } |
| |
| func (n nestedNamePart) template(args string) namePart { |
| return newTemplateNamePart(n, args) |
| } |
| |
| func (n nestedNamePart) prependName(prefix string) namePart { |
| return nestedNamePart{n.left, n.right.prependName(prefix)} |
| } |
| |
| func (n nestedNamePart) appendName(suffix string) namePart { |
| return nestedNamePart{n.left, n.right.appendName(suffix)} |
| } |
| |
| type templateNamePart struct { |
| tmpl namePart |
| args string |
| } |
| |
| var _ namePart = (*templateNamePart)(nil) |
| |
| func newTemplateNamePart(tmpl namePart, args string) namePart { |
| return templateNamePart{tmpl, args} |
| } |
| |
| func (n templateNamePart) String() string { |
| return fmt.Sprintf("%s<%s>", n.tmpl, n.args) |
| } |
| |
| func (n templateNamePart) Self() string { |
| return n.tmpl.Self() |
| } |
| |
| func (n templateNamePart) nest(name string) namePart { |
| return nestedNamePart{n, stringNamePart(name)} |
| } |
| |
| func (n templateNamePart) template(args string) namePart { |
| panic(fmt.Sprintf("Can't make a template of a template: %s", n)) |
| } |
| |
| func (n templateNamePart) prependName(prefix string) namePart { |
| panic(fmt.Sprintf("Can't prepend to the name of a template: %s", n)) |
| } |
| |
| func (n templateNamePart) appendName(suffix string) namePart { |
| panic(fmt.Sprintf("Can't append to the name of a template: %s", n)) |
| } |
| |
| // name holds a C++ qualified identifier. |
| // See: https://en.cppreference.com/w/cpp/language/identifiers#Qualified_identifiers |
| // It consists of a Namespace and a namePart. |
| // TODO(ianloic): move this to the top of the file since it's the most important type. |
| type name struct { |
| name namePart |
| ns namespace |
| } |
| |
| // makeName takes a string with a :: separated name and makes a Name treating the last component |
| // as the local name and the preceding components as the namespace. |
| // This should only be used with string literals for creating well-known, simple names. |
| func makeName(n string) name { |
| i := strings.LastIndex(n, "::") |
| if i == -1 { |
| return name{name: stringNamePart(n)} |
| } |
| if i == 0 { |
| panic(fmt.Sprintf("Don't call MakeName with leading double-colons: %v", n)) |
| } |
| return name{ |
| name: stringNamePart(n[i+2:]), |
| ns: newNamespace(n[0:i]), |
| } |
| } |
| |
| // simpleName return a name with a single component |
| func simpleName(n string) name { |
| if strings.ContainsAny(n, ":-./") { |
| panic(fmt.Sprintf("%#v is not a simple name", n)) |
| } |
| return name{name: stringNamePart(n)} |
| } |
| |
| // String returns the full name with a leading :: if the name has a namespace. |
| func (n name) String() string { |
| ns := n.ns.String() |
| if len(ns) > 0 { |
| ns = ns + "::" |
| } |
| return ns + n.name.String() |
| } |
| |
| // Name returns the portion of the name that comes after the namespace. |
| func (n name) Name() string { |
| return n.name.String() |
| } |
| |
| // Self returns how the type refers to itself, like in constructor & destructor names. |
| // For a nested name like "Foo::Bar::Baz" this would be "Baz". |
| // For a template name like "Foo::Bar<Baz>" this would be "Bar". |
| func (n name) Self() string { |
| return n.name.Self() |
| } |
| |
| // TODO(ianloic): probably make this the default |
| func (n name) NoLeading() string { |
| ns := n.ns.NoLeading() |
| if len(ns) > 0 { |
| ns = ns + "::" |
| } |
| return ns + n.name.String() |
| } |
| |
| // namespace returns the namespace portion of the name. |
| func (n name) Namespace() namespace { |
| return n.ns |
| } |
| |
| // nest returns a new name for a class nested inside the existing name. |
| func (n name) nest(nested string) name { |
| return name{name: n.name.nest(nested), ns: n.ns} |
| } |
| |
| // Template returns a new name with this name being an template applied to the |arg|. |
| func (n name) template(arg name) name { |
| return name{name: n.name.template(arg.String()), ns: n.ns} |
| } |
| |
| // arrayTemplate returns a new name with this name being an template applied to the |arg| with a |count|. |
| func (n name) arrayTemplate(arg name, count int) name { |
| return name{name: n.name.template(fmt.Sprintf("%s, %d", arg.String(), count)), ns: n.ns} |
| } |
| |
| // prependName returns a new name with a prefix prepended to the last part of the name. |
| func (n name) prependName(prefix string) name { |
| return name{name: n.name.prependName(prefix), ns: n.ns} |
| } |
| |
| // appendName returns a new name with a suffix appended to the last part of the name. |
| func (n name) appendName(suffix string) name { |
| return name{name: n.name.appendName(suffix), ns: n.ns} |
| } |
| |
| // appendNamespace returns a new name with an additional namespace component added. |
| func (n name) appendNamespace(part string) name { |
| return name{name: n.name, ns: n.ns.append(part)} |
| } |
| |
| // makeTupleName returns a Name for a std::tuple of the supplied names. |
| func makeTupleName(members []name) name { |
| t := makeName("std::tuple") |
| a := []string{} |
| for _, m := range members { |
| a = append(a, m.String()) |
| } |
| return name{name: t.name.template(strings.Join(a, ", ")), ns: t.ns} |
| } |