Home | History | Annotate | Download | only in chromeos-dbus-bindings
      1 // Copyright 2014 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chromeos-dbus-bindings/adaptor_generator.h"
      6 
      7 #include <string>
      8 
      9 #include <base/logging.h>
     10 #include <base/strings/string_util.h>
     11 #include <base/strings/stringprintf.h>
     12 #include <brillo/strings/string_utils.h>
     13 
     14 #include "chromeos-dbus-bindings/dbus_signature.h"
     15 #include "chromeos-dbus-bindings/indented_text.h"
     16 #include "chromeos-dbus-bindings/interface.h"
     17 #include "chromeos-dbus-bindings/name_parser.h"
     18 
     19 using base::StringPrintf;
     20 using std::string;
     21 using std::vector;
     22 
     23 namespace chromeos_dbus_bindings {
     24 
     25 // static
     26 bool AdaptorGenerator::GenerateAdaptors(
     27     const std::vector<Interface>& interfaces,
     28     const base::FilePath& output_file) {
     29   IndentedText text;
     30   CHECK(!interfaces.empty()) << "At least one interface must be provided";
     31 
     32   text.AddLine("// Automatic generation of D-Bus interfaces:");
     33   for (const auto& interface : interfaces) {
     34     text.AddLine(StringPrintf("//  - %s", interface.name.c_str()));
     35   }
     36   string header_guard = GenerateHeaderGuard(output_file);
     37   text.AddLine(StringPrintf("#ifndef %s", header_guard.c_str()));
     38   text.AddLine(StringPrintf("#define %s", header_guard.c_str()));
     39   text.AddLine("#include <memory>");
     40   text.AddLine("#include <string>");
     41   text.AddLine("#include <tuple>");
     42   text.AddLine("#include <vector>");
     43   text.AddBlankLine();
     44   text.AddLine("#include <base/macros.h>");
     45   text.AddLine("#include <dbus/object_path.h>");
     46   text.AddLine("#include <brillo/any.h>");
     47   text.AddLine("#include <brillo/dbus/dbus_object.h>");
     48   text.AddLine("#include <brillo/dbus/exported_object_manager.h>");
     49   text.AddLine("#include <brillo/variant_dictionary.h>");
     50 
     51   for (const auto& interface : interfaces)
     52     GenerateInterfaceAdaptor(interface, &text);
     53 
     54   text.AddLine(StringPrintf("#endif  // %s", header_guard.c_str()));
     55 
     56   return WriteTextToFile(output_file, text);
     57 }
     58 
     59 // static
     60 void AdaptorGenerator::GenerateInterfaceAdaptor(
     61     const Interface& interface,
     62     IndentedText *text) {
     63   NameParser parser{interface.name};
     64   string itf_name = parser.MakeInterfaceName(false);
     65   string class_name = parser.MakeAdaptorName(false);
     66   string full_itf_name = parser.MakeFullCppName();
     67 
     68   text->AddBlankLine();
     69   parser.AddOpenNamespaces(text, false);
     70 
     71   text->AddBlankLine();
     72   text->AddLine(StringPrintf("// Interface definition for %s.",
     73                              full_itf_name.c_str()));
     74   text->AddComments(interface.doc_string);
     75   text->AddLine(StringPrintf("class %s {", itf_name.c_str()));
     76   text->AddLineWithOffset("public:", kScopeOffset);
     77   text->PushOffset(kBlockOffset);
     78   text->AddLine(StringPrintf("virtual ~%s() = default;", itf_name.c_str()));
     79   AddInterfaceMethods(interface, text);
     80   text->PopOffset();
     81   text->AddLine("};");
     82 
     83   text->AddBlankLine();
     84   text->AddLine(StringPrintf("// Interface adaptor for %s.",
     85                              full_itf_name.c_str()));
     86   text->AddLine(StringPrintf("class %s {", class_name.c_str()));
     87   text->AddLineWithOffset("public:", kScopeOffset);
     88   text->PushOffset(kBlockOffset);
     89   AddConstructor(class_name, itf_name, text);
     90   AddRegisterWithDBusObject(itf_name, interface, text);
     91   AddSendSignalMethods(interface, text);
     92   AddPropertyMethodImplementation(interface, text);
     93   if (!interface.path.empty()) {
     94     text->AddBlankLine();
     95     text->AddLine("static dbus::ObjectPath GetObjectPath() {");
     96     text->PushOffset(kBlockOffset);
     97     text->AddLine(StringPrintf("return dbus::ObjectPath{\"%s\"};",
     98                                interface.path.c_str()));
     99     text->PopOffset();
    100     text->AddLine("}");
    101   }
    102   text->PopOffset();
    103 
    104   text->AddBlankLine();
    105   text->AddLineWithOffset("private:", kScopeOffset);
    106   text->PushOffset(kBlockOffset);
    107   AddSignalDataMembers(interface, text);
    108   AddPropertyDataMembers(interface, text);
    109 
    110   text->AddLine(StringPrintf(
    111       "%s* interface_;  // Owned by container of this adapter.",
    112       itf_name.c_str()));
    113 
    114   text->AddBlankLine();
    115   text->AddLine(StringPrintf("DISALLOW_COPY_AND_ASSIGN(%s);",
    116                              class_name.c_str()));
    117   text->PopOffset();
    118   text->AddLine("};");
    119 
    120   text->AddBlankLine();
    121   parser.AddCloseNamespaces(text, false);
    122 }
    123 
    124 // static
    125 void AdaptorGenerator::AddConstructor(const string& class_name,
    126                                       const string& itf_name,
    127                                       IndentedText *text) {
    128   text->AddLine(StringPrintf("%s(%s* interface) : interface_(interface) {}",
    129                              class_name.c_str(), itf_name.c_str()));
    130 }
    131 
    132 // static
    133 void AdaptorGenerator::AddRegisterWithDBusObject(
    134     const std::string& itf_name,
    135     const Interface& interface,
    136     IndentedText *text) {
    137   text->AddBlankLine();
    138   text->AddLine(
    139     "void RegisterWithDBusObject(brillo::dbus_utils::DBusObject* object) {");
    140   text->PushOffset(kBlockOffset);
    141   text->AddLine("brillo::dbus_utils::DBusInterface* itf =");
    142   text->AddLineWithOffset(
    143       StringPrintf("object->AddOrGetInterface(\"%s\");",
    144                    interface.name.c_str()), kLineContinuationOffset);
    145   RegisterInterface(itf_name, interface, text);
    146   text->PopOffset();
    147   text->AddLine("}");
    148 }
    149 
    150 // static
    151 void AdaptorGenerator::RegisterInterface(const string& itf_name,
    152                                          const Interface& interface,
    153                                          IndentedText *text) {
    154   if (!interface.methods.empty())
    155     text->AddBlankLine();
    156   for (const auto& method : interface.methods) {
    157     string add_handler_name;
    158     switch (method.kind) {
    159       case Interface::Method::Kind::kSimple:
    160         add_handler_name = "AddSimpleMethodHandler";
    161         break;
    162       case Interface::Method::Kind::kNormal:
    163         if (method.include_dbus_message)
    164           add_handler_name = "AddSimpleMethodHandlerWithErrorAndMessage";
    165         else
    166           add_handler_name = "AddSimpleMethodHandlerWithError";
    167         break;
    168       case Interface::Method::Kind::kAsync:
    169         if (method.include_dbus_message)
    170           add_handler_name = "AddMethodHandlerWithMessage";
    171         else
    172           add_handler_name = "AddMethodHandler";
    173         break;
    174       case Interface::Method::Kind::kRaw:
    175         add_handler_name = "AddRawMethodHandler";
    176         break;
    177     }
    178 
    179     text->AddLine(StringPrintf("itf->%s(", add_handler_name.c_str()));
    180     text->PushOffset(kLineContinuationOffset);
    181     text->AddLine(StringPrintf("\"%s\",", method.name.c_str()));
    182     text->AddLine("base::Unretained(interface_),");
    183     text->AddLine(StringPrintf("&%s::%s);", itf_name.c_str(),
    184                                method.name.c_str()));
    185     text->PopOffset();
    186   }
    187 
    188   // Register signals.
    189   if (!interface.signals.empty())
    190     text->AddBlankLine();
    191   for (const auto& signal : interface.signals) {
    192     string signal_var_name = StringPrintf("signal_%s_", signal.name.c_str());
    193     string signal_type_name = StringPrintf("Signal%sType", signal.name.c_str());
    194     text->AddLine(StringPrintf("%s = itf->RegisterSignalOfType<%s>(\"%s\");",
    195                                signal_var_name.c_str(),
    196                                signal_type_name.c_str(),
    197                                signal.name.c_str()));
    198   }
    199 
    200   // Register exported properties.
    201   if (!interface.properties.empty())
    202     text->AddBlankLine();
    203   for (const auto& property : interface.properties) {
    204     string variable_name = NameParser{property.name}.MakeVariableName();
    205     string write_access;
    206     if (property.access == "write") {
    207       write_access = "kWriteOnly";
    208     } else if (property.access == "readwrite") {
    209       write_access = "kReadWrite";
    210     }
    211     if (!write_access.empty()) {
    212       text->AddLine(StringPrintf("%s_.SetAccessMode(", variable_name.c_str()));
    213       text->PushOffset(kLineContinuationOffset);
    214       text->AddLine(
    215           StringPrintf(
    216               "brillo::dbus_utils::ExportedPropertyBase::Access::%s);",
    217               write_access.c_str()));
    218       text->PopOffset();
    219       text->AddLine(StringPrintf("%s_.SetValidator(", variable_name.c_str()));
    220       text->PushOffset(kLineContinuationOffset);
    221       text->AddLineAndPushOffsetTo(
    222           StringPrintf(
    223               "base::Bind(&%s::Validate%s,",
    224               NameParser{interface.name}.MakeAdaptorName(false).c_str(),
    225               property.name.c_str()),
    226           1, '(');
    227       text->AddLine("base::Unretained(this)));");
    228       text->PopOffset();
    229       text->PopOffset();
    230     }
    231     text->AddLine(StringPrintf("itf->AddProperty(%sName(), &%s_);",
    232                                property.name.c_str(), variable_name.c_str()));
    233   }
    234 }
    235 
    236 // static
    237 void AdaptorGenerator::AddInterfaceMethods(const Interface& interface,
    238                                            IndentedText *text) {
    239   IndentedText block;
    240   DbusSignature signature;
    241   if (!interface.methods.empty())
    242     block.AddBlankLine();
    243 
    244   for (const auto& method : interface.methods) {
    245     string const_method;
    246     if (method.is_const)
    247       const_method = " const";
    248 
    249     string return_type = "void";
    250     vector<string> method_params;
    251     auto input_arguments_copy = method.input_arguments;
    252     auto output_arguments_copy = method.output_arguments;
    253     switch (method.kind) {
    254       case Interface::Method::Kind::kSimple:
    255         if (output_arguments_copy.size() == 1) {
    256           CHECK(signature.Parse(output_arguments_copy[0].type, &return_type));
    257           output_arguments_copy.clear();
    258         }
    259         break;
    260       case Interface::Method::Kind::kNormal:
    261         method_params.push_back("brillo::ErrorPtr* error");
    262         if (method.include_dbus_message)
    263           method_params.push_back("dbus::Message* message");
    264         return_type = "bool";
    265         break;
    266       case Interface::Method::Kind::kAsync: {
    267         std::vector<std::string> out_types;
    268         for (const auto& argument : output_arguments_copy) {
    269           string param_type;
    270           CHECK(signature.Parse(argument.type, &param_type));
    271           out_types.push_back(param_type);
    272         }
    273         method_params.push_back(base::StringPrintf(
    274             "std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<%s>> "
    275             "response",
    276              brillo::string_utils::Join(", ", out_types).c_str()));
    277         if (method.include_dbus_message)
    278           method_params.push_back("dbus::Message* message");
    279         output_arguments_copy.clear();
    280         break;
    281       }
    282       case Interface::Method::Kind::kRaw:
    283         method_params.push_back("dbus::MethodCall* method_call");
    284         method_params.push_back("brillo::dbus_utils::ResponseSender sender");
    285         // Raw methods don't take static parameters or return values directly.
    286         input_arguments_copy.clear();
    287         output_arguments_copy.clear();
    288         break;
    289     }
    290     block.AddComments(method.doc_string);
    291     string method_start = StringPrintf("virtual %s %s(",
    292                                        return_type.c_str(),
    293                                        method.name.c_str());
    294     string method_end = StringPrintf(")%s = 0;", const_method.c_str());
    295     int index = 0;
    296     for (const auto& argument : input_arguments_copy) {
    297       string param_type;
    298       CHECK(signature.Parse(argument.type, &param_type));
    299       MakeConstReferenceIfNeeded(&param_type);
    300       string param_name = GetArgName("in", argument.name, ++index);
    301       method_params.push_back(param_type + ' ' + param_name);
    302     }
    303 
    304     for (const auto& argument : output_arguments_copy) {
    305       string param_type;
    306       CHECK(signature.Parse(argument.type, &param_type));
    307       string param_name = GetArgName("out", argument.name, ++index);
    308       method_params.push_back(param_type + "* " + param_name);
    309     }
    310 
    311     if (method_params.empty()) {
    312       block.AddLine(method_start + method_end);
    313     } else {
    314       block.AddLine(method_start);
    315       block.PushOffset(kLineContinuationOffset);
    316       for (size_t i = 0; i < method_params.size() - 1; i++)
    317         block.AddLine(method_params[i] + ',');
    318       block.AddLine(method_params.back() + method_end);
    319       block.PopOffset();
    320     }
    321   }
    322   text->AddBlock(block);
    323 }
    324 
    325 // static
    326 void AdaptorGenerator::AddSendSignalMethods(
    327     const Interface& interface,
    328     IndentedText *text) {
    329   IndentedText block;
    330   DbusSignature signature;
    331 
    332   if (!interface.signals.empty())
    333     block.AddBlankLine();
    334 
    335   for (const auto& signal : interface.signals) {
    336     block.AddComments(signal.doc_string);
    337     string method_start = StringPrintf("void Send%sSignal(",
    338                                        signal.name.c_str());
    339     string method_end = ") {";
    340 
    341     int index = 0;
    342     vector<string> method_params;
    343     vector<string> param_names;
    344     for (const auto& argument : signal.arguments) {
    345       string param_type;
    346       CHECK(signature.Parse(argument.type, &param_type));
    347       MakeConstReferenceIfNeeded(&param_type);
    348       string param_name = GetArgName("in", argument.name, ++index);
    349       param_names.push_back(param_name);
    350       method_params.push_back(param_type + ' ' + param_name);
    351     }
    352 
    353     if (method_params.empty()) {
    354       block.AddLine(method_start + method_end);
    355     } else {
    356       block.AddLine(method_start);
    357       block.PushOffset(kLineContinuationOffset);
    358       for (size_t i = 0; i < method_params.size() - 1; i++)
    359         block.AddLine(method_params[i] + ',');
    360       block.AddLine(method_params.back() + method_end);
    361       block.PopOffset();
    362     }
    363 
    364     string args = brillo::string_utils::Join(", ", param_names);
    365     block.PushOffset(kBlockOffset);
    366     block.AddLine(StringPrintf("auto signal = signal_%s_.lock();",
    367                                 signal.name.c_str()));
    368     block.AddLine("if (signal)");
    369     block.AddLineWithOffset(StringPrintf("signal->Send(%s);", args.c_str()),
    370                             kBlockOffset);
    371     block.PopOffset();
    372     block.AddLine("}");
    373   }
    374   text->AddBlock(block);
    375 }
    376 
    377 // static
    378 void AdaptorGenerator::AddSignalDataMembers(const Interface& interface,
    379                                             IndentedText *text) {
    380   IndentedText block;
    381   DbusSignature signature;
    382 
    383   for (const auto& signal : interface.signals) {
    384     string signal_type_name = StringPrintf("Signal%sType", signal.name.c_str());
    385     string signal_type_alias_begin =
    386         StringPrintf("using %s = brillo::dbus_utils::DBusSignal<",
    387                      signal_type_name.c_str());
    388     string signal_type_alias_end = ">;";
    389     vector<string> params;
    390     for (const auto& argument : signal.arguments) {
    391       string param;
    392       CHECK(signature.Parse(argument.type, &param));
    393       if (!argument.name.empty())
    394         base::StringAppendF(&param, " /*%s*/", argument.name.c_str());
    395       params.push_back(param);
    396     }
    397     if (params.empty()) {
    398       block.AddLine(signal_type_alias_begin + signal_type_alias_end);
    399     } else {
    400       block.AddLine(signal_type_alias_begin);
    401       block.PushOffset(kLineContinuationOffset);
    402       for (size_t i = 0; i < params.size() - 1; i++)
    403         block.AddLine(params[i] + ',');
    404       block.AddLine(params.back() + signal_type_alias_end);
    405       block.PopOffset();
    406     }
    407     block.AddLine(
    408         StringPrintf("std::weak_ptr<%s> signal_%s_;",
    409                       signal_type_name.c_str(), signal.name.c_str()));
    410     block.AddBlankLine();
    411   }
    412   text->AddBlock(block);
    413 }
    414 
    415 // static
    416 void AdaptorGenerator::AddPropertyMethodImplementation(
    417     const Interface& interface,
    418     IndentedText *text) {
    419   IndentedText block;
    420   DbusSignature signature;
    421 
    422   for (const auto& property : interface.properties) {
    423     block.AddBlankLine();
    424     string type;
    425     CHECK(signature.Parse(property.type, &type));
    426     string variable_name = NameParser{property.name}.MakeVariableName();
    427 
    428     // Property name accessor.
    429     block.AddComments(property.doc_string);
    430     block.AddLine(StringPrintf("static const char* %sName() { return \"%s\"; }",
    431                                property.name.c_str(), property.name.c_str()));
    432 
    433     // Getter method.
    434     block.AddLine(StringPrintf("%s Get%s() const {",
    435                                type.c_str(),
    436                                property.name.c_str()));
    437     block.PushOffset(kBlockOffset);
    438     block.AddLine(StringPrintf("return %s_.GetValue().Get<%s>();",
    439                                variable_name.c_str(),
    440                                type.c_str()));
    441     block.PopOffset();
    442     block.AddLine("}");
    443 
    444     // Setter method.
    445     MakeConstReferenceIfNeeded(&type);
    446     block.AddLine(StringPrintf("void Set%s(%s %s) {",
    447                                property.name.c_str(),
    448                                type.c_str(),
    449                                variable_name.c_str()));
    450     block.PushOffset(kBlockOffset);
    451     block.AddLine(StringPrintf("%s_.SetValue(%s);",
    452                                variable_name.c_str(),
    453                                variable_name.c_str()));
    454     block.PopOffset();
    455     block.AddLine("}");
    456 
    457     // Validation method for property with write access.
    458     if (property.access != "read") {
    459       CHECK(signature.Parse(property.type, &type));
    460       block.AddLine(StringPrintf("virtual bool Validate%s(",
    461                                  property.name.c_str()));
    462       block.PushOffset(kLineContinuationOffset);
    463       // Explicitly specify the "value" parameter as const & to match the
    464       // validator callback function signature.
    465       block.AddLine(
    466           StringPrintf(
    467               "brillo::ErrorPtr* /*error*/, const %s& /*value*/) {",
    468               type.c_str()));
    469       block.PopOffset();
    470       block.PushOffset(kBlockOffset);
    471       block.AddLine("return true;");
    472       block.PopOffset();
    473       block.AddLine("}");
    474     }
    475   }
    476   text->AddBlock(block);
    477 }
    478 
    479 // static
    480 void AdaptorGenerator::AddPropertyDataMembers(const Interface& interface,
    481                                               IndentedText *text) {
    482   IndentedText block;
    483   DbusSignature signature;
    484 
    485   for (const auto& property : interface.properties) {
    486     string type;
    487     CHECK(signature.Parse(property.type, &type));
    488     string variable_name = NameParser{property.name}.MakeVariableName();
    489 
    490     block.AddLine(
    491         StringPrintf("brillo::dbus_utils::ExportedProperty<%s> %s_;",
    492                      type.c_str(), variable_name.c_str()));
    493   }
    494   if (!interface.properties.empty())
    495     block.AddBlankLine();
    496 
    497   text->AddBlock(block);
    498 }
    499 
    500 }  // namespace chromeos_dbus_bindings
    501