// 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(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);
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 string& class_name,
const string& itf_name,
IndentedText *text) {
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