| // Copyright 2014 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromeos-dbus-bindings/adaptor_generator.h" |
| |
| #include <string> |
| |
| #include <base/logging.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <brillo/strings/string_utils.h> |
| |
| #include "chromeos-dbus-bindings/dbus_signature.h" |
| #include "chromeos-dbus-bindings/indented_text.h" |
| #include "chromeos-dbus-bindings/interface.h" |
| #include "chromeos-dbus-bindings/name_parser.h" |
| |
| using base::StringPrintf; |
| using std::string; |
| using std::vector; |
| |
| namespace chromeos_dbus_bindings { |
| |
| // static |
| bool AdaptorGenerator::GenerateAdaptors( |
| const std::vector<Interface>& interfaces, |
| const base::FilePath& output_file) { |
| IndentedText text; |
| CHECK(!interfaces.empty()) << "At least one interface must be provided"; |
| |
| text.AddLine("// Automatic generation of D-Bus interfaces:"); |
| for (const auto& interface : interfaces) { |
| text.AddLine(StringPrintf("// - %s", interface.name.c_str())); |
| } |
| string header_guard = GenerateHeaderGuard(output_file); |
| text.AddLine(StringPrintf("#ifndef %s", header_guard.c_str())); |
| text.AddLine(StringPrintf("#define %s", header_guard.c_str())); |
| text.AddLine("#include <memory>"); |
| text.AddLine("#include <string>"); |
| text.AddLine("#include <tuple>"); |
| text.AddLine("#include <vector>"); |
| text.AddBlankLine(); |
| text.AddLine("#include <base/macros.h>"); |
| text.AddLine("#include <dbus/object_path.h>"); |
| text.AddLine("#include <brillo/any.h>"); |
| text.AddLine("#include <brillo/dbus/dbus_object.h>"); |
| text.AddLine("#include <brillo/dbus/exported_object_manager.h>"); |
| text.AddLine("#include <brillo/variant_dictionary.h>"); |
| |
| for (const auto& interface : interfaces) |
| GenerateInterfaceAdaptor(interface, &text); |
| |
| text.AddLine(StringPrintf("#endif // %s", header_guard.c_str())); |
| |
| return WriteTextToFile(output_file, text); |
| } |
| |
| // static |
| void AdaptorGenerator::GenerateInterfaceAdaptor( |
| const Interface& interface, |
| IndentedText *text) { |
| NameParser parser{interface.name}; |
| string itf_name = parser.MakeInterfaceName(false); |
| string class_name = parser.MakeAdaptorName(false); |
| string full_itf_name = parser.MakeFullCppName(); |
| |
| text->AddBlankLine(); |
| parser.AddOpenNamespaces(text, false); |
| |
| text->AddBlankLine(); |
| text->AddLine(StringPrintf("// Interface definition for %s.", |
| full_itf_name.c_str())); |
| text->AddComments(interface.doc_string); |
| text->AddLine(StringPrintf("class %s {", itf_name.c_str())); |
| text->AddLineWithOffset("public:", kScopeOffset); |
| text->PushOffset(kBlockOffset); |
| text->AddLine(StringPrintf("virtual ~%s() = default;", itf_name.c_str())); |
| AddInterfaceMethods(interface, text); |
| text->PopOffset(); |
| text->AddLine("};"); |
| |
| text->AddBlankLine(); |
| text->AddLine(StringPrintf("// Interface adaptor for %s.", |
| full_itf_name.c_str())); |
| text->AddLine(StringPrintf("class %s {", class_name.c_str())); |
| text->AddLineWithOffset("public:", kScopeOffset); |
| text->PushOffset(kBlockOffset); |
| AddConstructor(interface, class_name, itf_name, text); |
| AddRegisterWithDBusObject(itf_name, interface, text); |
| AddSendSignalMethods(interface, text); |
| AddPropertyMethodImplementation(interface, text); |
| if (!interface.path.empty()) { |
| text->AddBlankLine(); |
| text->AddLine("static dbus::ObjectPath GetObjectPath() {"); |
| text->PushOffset(kBlockOffset); |
| text->AddLine(StringPrintf("return dbus::ObjectPath{\"%s\"};", |
| interface.path.c_str())); |
| text->PopOffset(); |
| text->AddLine("}"); |
| } |
| text->PopOffset(); |
| |
| text->AddBlankLine(); |
| text->AddLineWithOffset("private:", kScopeOffset); |
| text->PushOffset(kBlockOffset); |
| AddSignalDataMembers(interface, text); |
| AddPropertyDataMembers(interface, text); |
| |
| if (!interface.methods.empty()) { |
| text->AddLine(StringPrintf( |
| "%s* interface_; // Owned by container of this adapter.", |
| itf_name.c_str())); |
| } |
| |
| text->AddBlankLine(); |
| text->AddLine(StringPrintf("DISALLOW_COPY_AND_ASSIGN(%s);", |
| class_name.c_str())); |
| text->PopOffset(); |
| text->AddLine("};"); |
| |
| text->AddBlankLine(); |
| parser.AddCloseNamespaces(text, false); |
| } |
| |
| // static |
| void AdaptorGenerator::AddConstructor(const Interface& interface, |
| const string& class_name, |
| const string& itf_name, |
| IndentedText *text) { |
| if (interface.methods.empty()) { |
| text->AddLine(StringPrintf("%s(%s* /* interface */) {}", |
| class_name.c_str(), itf_name.c_str())); |
| |
| } else { |
| text->AddLine(StringPrintf("%s(%s* interface) : interface_(interface) {}", |
| class_name.c_str(), itf_name.c_str())); |
| } |
| } |
| |
| // static |
| void AdaptorGenerator::AddRegisterWithDBusObject( |
| const std::string& itf_name, |
| const Interface& interface, |
| IndentedText *text) { |
| text->AddBlankLine(); |
| text->AddLine( |
| "void RegisterWithDBusObject(brillo::dbus_utils::DBusObject* object) {"); |
| text->PushOffset(kBlockOffset); |
| text->AddLine("brillo::dbus_utils::DBusInterface* itf ="); |
| text->AddLineWithOffset( |
| StringPrintf("object->AddOrGetInterface(\"%s\");", |
| interface.name.c_str()), kLineContinuationOffset); |
| RegisterInterface(itf_name, interface, text); |
| text->PopOffset(); |
| text->AddLine("}"); |
| } |
| |
| // static |
| void AdaptorGenerator::RegisterInterface(const string& itf_name, |
| const Interface& interface, |
| IndentedText *text) { |
| if (!interface.methods.empty()) |
| text->AddBlankLine(); |
| for (const auto& method : interface.methods) { |
| string add_handler_name; |
| switch (method.kind) { |
| case Interface::Method::Kind::kSimple: |
| add_handler_name = "AddSimpleMethodHandler"; |
| break; |
| case Interface::Method::Kind::kNormal: |
| if (method.include_dbus_message) |
| add_handler_name = "AddSimpleMethodHandlerWithErrorAndMessage"; |
| else |
| add_handler_name = "AddSimpleMethodHandlerWithError"; |
| break; |
| case Interface::Method::Kind::kAsync: |
| if (method.include_dbus_message) |
| add_handler_name = "AddMethodHandlerWithMessage"; |
| else |
| add_handler_name = "AddMethodHandler"; |
| break; |
| case Interface::Method::Kind::kRaw: |
| add_handler_name = "AddRawMethodHandler"; |
| break; |
| } |
| |
| text->AddLine(StringPrintf("itf->%s(", add_handler_name.c_str())); |
| text->PushOffset(kLineContinuationOffset); |
| text->AddLine(StringPrintf("\"%s\",", method.name.c_str())); |
| text->AddLine("base::Unretained(interface_),"); |
| text->AddLine(StringPrintf("&%s::%s);", itf_name.c_str(), |
| method.name.c_str())); |
| text->PopOffset(); |
| } |
| |
| // Register signals. |
| if (!interface.signals.empty()) |
| text->AddBlankLine(); |
| for (const auto& signal : interface.signals) { |
| string signal_var_name = StringPrintf("signal_%s_", signal.name.c_str()); |
| string signal_type_name = StringPrintf("Signal%sType", signal.name.c_str()); |
| text->AddLine(StringPrintf("%s = itf->RegisterSignalOfType<%s>(\"%s\");", |
| signal_var_name.c_str(), |
| signal_type_name.c_str(), |
| signal.name.c_str())); |
| } |
| |
| // Register exported properties. |
| if (!interface.properties.empty()) |
| text->AddBlankLine(); |
| for (const auto& property : interface.properties) { |
| string variable_name = NameParser{property.name}.MakeVariableName(); |
| string write_access; |
| if (property.access == "write") { |
| write_access = "kWriteOnly"; |
| } else if (property.access == "readwrite") { |
| write_access = "kReadWrite"; |
| } |
| if (!write_access.empty()) { |
| text->AddLine(StringPrintf("%s_.SetAccessMode(", variable_name.c_str())); |
| text->PushOffset(kLineContinuationOffset); |
| text->AddLine( |
| StringPrintf( |
| "brillo::dbus_utils::ExportedPropertyBase::Access::%s);", |
| write_access.c_str())); |
| text->PopOffset(); |
| text->AddLine(StringPrintf("%s_.SetValidator(", variable_name.c_str())); |
| text->PushOffset(kLineContinuationOffset); |
| text->AddLineAndPushOffsetTo( |
| StringPrintf( |
| "base::Bind(&%s::Validate%s,", |
| NameParser{interface.name}.MakeAdaptorName(false).c_str(), |
| property.name.c_str()), |
| 1, '('); |
| text->AddLine("base::Unretained(this)));"); |
| text->PopOffset(); |
| text->PopOffset(); |
| } |
| text->AddLine(StringPrintf("itf->AddProperty(%sName(), &%s_);", |
| property.name.c_str(), variable_name.c_str())); |
| } |
| } |
| |
| // static |
| void AdaptorGenerator::AddInterfaceMethods(const Interface& interface, |
| IndentedText *text) { |
| IndentedText block; |
| DbusSignature signature; |
| if (!interface.methods.empty()) |
| block.AddBlankLine(); |
| |
| for (const auto& method : interface.methods) { |
| string const_method; |
| if (method.is_const) |
| const_method = " const"; |
| |
| string return_type = "void"; |
| vector<string> method_params; |
| auto input_arguments_copy = method.input_arguments; |
| auto output_arguments_copy = method.output_arguments; |
| switch (method.kind) { |
| case Interface::Method::Kind::kSimple: |
| if (output_arguments_copy.size() == 1) { |
| CHECK(signature.Parse(output_arguments_copy[0].type, &return_type)); |
| output_arguments_copy.clear(); |
| } |
| break; |
| case Interface::Method::Kind::kNormal: |
| method_params.push_back("brillo::ErrorPtr* error"); |
| if (method.include_dbus_message) |
| method_params.push_back("dbus::Message* message"); |
| return_type = "bool"; |
| break; |
| case Interface::Method::Kind::kAsync: { |
| std::vector<std::string> out_types; |
| for (const auto& argument : output_arguments_copy) { |
| string param_type; |
| CHECK(signature.Parse(argument.type, ¶m_type)); |
| out_types.push_back(param_type); |
| } |
| method_params.push_back(base::StringPrintf( |
| "std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<%s>> " |
| "response", |
| brillo::string_utils::Join(", ", out_types).c_str())); |
| if (method.include_dbus_message) |
| method_params.push_back("dbus::Message* message"); |
| output_arguments_copy.clear(); |
| break; |
| } |
| case Interface::Method::Kind::kRaw: |
| method_params.push_back("dbus::MethodCall* method_call"); |
| method_params.push_back("brillo::dbus_utils::ResponseSender sender"); |
| // Raw methods don't take static parameters or return values directly. |
| input_arguments_copy.clear(); |
| output_arguments_copy.clear(); |
| break; |
| } |
| block.AddComments(method.doc_string); |
| string method_start = StringPrintf("virtual %s %s(", |
| return_type.c_str(), |
| method.name.c_str()); |
| string method_end = StringPrintf(")%s = 0;", const_method.c_str()); |
| int index = 0; |
| for (const auto& argument : input_arguments_copy) { |
| string param_type; |
| CHECK(signature.Parse(argument.type, ¶m_type)); |
| MakeConstReferenceIfNeeded(¶m_type); |
| string param_name = GetArgName("in", argument.name, ++index); |
| method_params.push_back(param_type + ' ' + param_name); |
| } |
| |
| for (const auto& argument : output_arguments_copy) { |
| string param_type; |
| CHECK(signature.Parse(argument.type, ¶m_type)); |
| string param_name = GetArgName("out", argument.name, ++index); |
| method_params.push_back(param_type + "* " + param_name); |
| } |
| |
| if (method_params.empty()) { |
| block.AddLine(method_start + method_end); |
| } else { |
| block.AddLine(method_start); |
| block.PushOffset(kLineContinuationOffset); |
| for (size_t i = 0; i < method_params.size() - 1; i++) |
| block.AddLine(method_params[i] + ','); |
| block.AddLine(method_params.back() + method_end); |
| block.PopOffset(); |
| } |
| } |
| text->AddBlock(block); |
| } |
| |
| // static |
| void AdaptorGenerator::AddSendSignalMethods( |
| const Interface& interface, |
| IndentedText *text) { |
| IndentedText block; |
| DbusSignature signature; |
| |
| if (!interface.signals.empty()) |
| block.AddBlankLine(); |
| |
| for (const auto& signal : interface.signals) { |
| block.AddComments(signal.doc_string); |
| string method_start = StringPrintf("void Send%sSignal(", |
| signal.name.c_str()); |
| string method_end = ") {"; |
| |
| int index = 0; |
| vector<string> method_params; |
| vector<string> param_names; |
| for (const auto& argument : signal.arguments) { |
| string param_type; |
| CHECK(signature.Parse(argument.type, ¶m_type)); |
| MakeConstReferenceIfNeeded(¶m_type); |
| string param_name = GetArgName("in", argument.name, ++index); |
| param_names.push_back(param_name); |
| method_params.push_back(param_type + ' ' + param_name); |
| } |
| |
| if (method_params.empty()) { |
| block.AddLine(method_start + method_end); |
| } else { |
| block.AddLine(method_start); |
| block.PushOffset(kLineContinuationOffset); |
| for (size_t i = 0; i < method_params.size() - 1; i++) |
| block.AddLine(method_params[i] + ','); |
| block.AddLine(method_params.back() + method_end); |
| block.PopOffset(); |
| } |
| |
| string args = brillo::string_utils::Join(", ", param_names); |
| block.PushOffset(kBlockOffset); |
| block.AddLine(StringPrintf("auto signal = signal_%s_.lock();", |
| signal.name.c_str())); |
| block.AddLine("if (signal)"); |
| block.AddLineWithOffset(StringPrintf("signal->Send(%s);", args.c_str()), |
| kBlockOffset); |
| block.PopOffset(); |
| block.AddLine("}"); |
| } |
| text->AddBlock(block); |
| } |
| |
| // static |
| void AdaptorGenerator::AddSignalDataMembers(const Interface& interface, |
| IndentedText *text) { |
| IndentedText block; |
| DbusSignature signature; |
| |
| for (const auto& signal : interface.signals) { |
| string signal_type_name = StringPrintf("Signal%sType", signal.name.c_str()); |
| string signal_type_alias_begin = |
| StringPrintf("using %s = brillo::dbus_utils::DBusSignal<", |
| signal_type_name.c_str()); |
| string signal_type_alias_end = ">;"; |
| vector<string> params; |
| for (const auto& argument : signal.arguments) { |
| string param; |
| CHECK(signature.Parse(argument.type, ¶m)); |
| if (!argument.name.empty()) |
| base::StringAppendF(¶m, " /*%s*/", argument.name.c_str()); |
| params.push_back(param); |
| } |
| if (params.empty()) { |
| block.AddLine(signal_type_alias_begin + signal_type_alias_end); |
| } else { |
| block.AddLine(signal_type_alias_begin); |
| block.PushOffset(kLineContinuationOffset); |
| for (size_t i = 0; i < params.size() - 1; i++) |
| block.AddLine(params[i] + ','); |
| block.AddLine(params.back() + signal_type_alias_end); |
| block.PopOffset(); |
| } |
| block.AddLine( |
| StringPrintf("std::weak_ptr<%s> signal_%s_;", |
| signal_type_name.c_str(), signal.name.c_str())); |
| block.AddBlankLine(); |
| } |
| text->AddBlock(block); |
| } |
| |
| // static |
| void AdaptorGenerator::AddPropertyMethodImplementation( |
| const Interface& interface, |
| IndentedText *text) { |
| IndentedText block; |
| DbusSignature signature; |
| |
| for (const auto& property : interface.properties) { |
| block.AddBlankLine(); |
| string type; |
| CHECK(signature.Parse(property.type, &type)); |
| string variable_name = NameParser{property.name}.MakeVariableName(); |
| |
| // Property name accessor. |
| block.AddComments(property.doc_string); |
| block.AddLine(StringPrintf("static const char* %sName() { return \"%s\"; }", |
| property.name.c_str(), property.name.c_str())); |
| |
| // Getter method. |
| block.AddLine(StringPrintf("%s Get%s() const {", |
| type.c_str(), |
| property.name.c_str())); |
| block.PushOffset(kBlockOffset); |
| block.AddLine(StringPrintf("return %s_.GetValue().Get<%s>();", |
| variable_name.c_str(), |
| type.c_str())); |
| block.PopOffset(); |
| block.AddLine("}"); |
| |
| // Setter method. |
| MakeConstReferenceIfNeeded(&type); |
| block.AddLine(StringPrintf("void Set%s(%s %s) {", |
| property.name.c_str(), |
| type.c_str(), |
| variable_name.c_str())); |
| block.PushOffset(kBlockOffset); |
| block.AddLine(StringPrintf("%s_.SetValue(%s);", |
| variable_name.c_str(), |
| variable_name.c_str())); |
| block.PopOffset(); |
| block.AddLine("}"); |
| |
| // Validation method for property with write access. |
| if (property.access != "read") { |
| CHECK(signature.Parse(property.type, &type)); |
| block.AddLine(StringPrintf("virtual bool Validate%s(", |
| property.name.c_str())); |
| block.PushOffset(kLineContinuationOffset); |
| // Explicitly specify the "value" parameter as const & to match the |
| // validator callback function signature. |
| block.AddLine( |
| StringPrintf( |
| "brillo::ErrorPtr* /*error*/, const %s& /*value*/) {", |
| type.c_str())); |
| block.PopOffset(); |
| block.PushOffset(kBlockOffset); |
| block.AddLine("return true;"); |
| block.PopOffset(); |
| block.AddLine("}"); |
| } |
| } |
| text->AddBlock(block); |
| } |
| |
| // static |
| void AdaptorGenerator::AddPropertyDataMembers(const Interface& interface, |
| IndentedText *text) { |
| IndentedText block; |
| DbusSignature signature; |
| |
| for (const auto& property : interface.properties) { |
| string type; |
| CHECK(signature.Parse(property.type, &type)); |
| string variable_name = NameParser{property.name}.MakeVariableName(); |
| |
| block.AddLine( |
| StringPrintf("brillo::dbus_utils::ExportedProperty<%s> %s_;", |
| type.c_str(), variable_name.c_str())); |
| } |
| if (!interface.properties.empty()) |
| block.AddBlankLine(); |
| |
| text->AddBlock(block); |
| } |
| |
| } // namespace chromeos_dbus_bindings |