blob: 53f7e79d623069489a4ee9832156c30fed0fa166 [file] [log] [blame]
// 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/stringprintf.h>
#include "chromeos-dbus-bindings/dbus_signature.h"
#include "chromeos-dbus-bindings/indented_text.h"
#include "chromeos-dbus-bindings/interface.h"
using base::StringPrintf;
using std::string;
using std::vector;
namespace chromeos_dbus_bindings {
// static
bool AdaptorGenerator::GenerateAdaptor(
const Interface& interface,
const base::FilePath& output_file) {
IndentedText text;
text.AddLine(StringPrintf("// Automatic generation of interface for %s",
interface.name.c_str()));
string header_guard = GenerateHeaderGuard(output_file, interface.name);
text.AddLine(StringPrintf("#ifndef %s", header_guard.c_str()));
text.AddLine(StringPrintf("#define %s", header_guard.c_str()));
text.AddLine("#include <string>");
text.AddLine("#include <vector>");
text.AddBlankLine();
text.AddLine("#include <base/macros.h>");
text.AddLine("#include <dbus/object_path.h>");
text.AddLine("#include <chromeos/any.h>");
text.AddLine("#include <chromeos/dbus/dbus_object.h>");
text.AddLine("#include <chromeos/dbus/exported_object_manager.h>");
text.AddLine("#include <chromeos/variant_dictionary.h>");
text.AddBlankLine();
vector<string> namespaces;
string class_name;
CHECK(GetNamespacesAndClassName(interface.name, &namespaces, &class_name));
for (const auto& space : namespaces) {
text.AddLine(StringPrintf("namespace %s {", space.c_str()));
}
text.AddBlankLine();
string adaptor_name = StringPrintf("%sAdaptor", class_name.c_str());
text.AddLine(StringPrintf("class %s {", adaptor_name.c_str()));
text.AddLineWithOffset("public:", kScopeOffset);
text.PushOffset(kBlockOffset);
AddMethodInterface(interface, &text);
AddConstructor(interface, adaptor_name, &text);
AddSendSignalMethods(interface, &text);
text.AddLine(StringPrintf("virtual ~%s() = default;", adaptor_name.c_str()));
text.AddLine("virtual void OnRegisterComplete(bool success) {}");
text.PopOffset();
text.AddBlankLine();
text.AddLineWithOffset("protected:", kScopeOffset);
text.PushOffset(kBlockOffset);
text.AddLine("chromeos::dbus_utils::DBusInterface* dbus_interface() {");
text.PushOffset(kBlockOffset);
text.AddLine("return dbus_interface_;");
text.PopOffset();
text.AddLine("}");
text.PopOffset();
text.AddBlankLine();
text.AddLineWithOffset("private:", kScopeOffset);
text.PushOffset(kBlockOffset);
text.AddLine("MethodInterface* interface_; // Owned by caller.");
text.AddLine("chromeos::dbus_utils::DBusObject dbus_object_;");
AddSignalDataMembers(interface, &text);
text.AddLine("// Owned by |dbus_object_|.");
text.AddLine("chromeos::dbus_utils::DBusInterface* dbus_interface_;");
text.AddLine(StringPrintf(
"DISALLOW_COPY_AND_ASSIGN(%s);", adaptor_name.c_str()));
text.PopOffset();
text.AddLine("};");
text.AddBlankLine();
for (auto it = namespaces.rbegin(); it != namespaces.rend(); ++it) {
text.AddLine(StringPrintf("} // namespace %s", it->c_str()));
}
text.AddLine(StringPrintf("#endif // %s", header_guard.c_str()));
return WriteTextToFile(output_file, text);
}
// static
void AdaptorGenerator::AddConstructor(const Interface& interface,
const string& class_name,
IndentedText *text) {
IndentedText block;
block.AddLine(StringPrintf("%s(", class_name.c_str()));
block.PushOffset(kLineContinuationOffset);
block.AddLine("chromeos::dbus_utils::ExportedObjectManager* object_manager,");
block.AddLine("const scoped_refptr<dbus::Bus>& bus,");
block.AddLine("const std::string& object_path,");
block.AddLine("MethodInterface* interface) // Owned by caller.");
block.AddLine(": interface_(interface),");
block.PushOffset(kBlockOffset);
block.AddLine("dbus_object_(");
block.PushOffset(kLineContinuationOffset);
block.AddLine("object_manager,");
block.AddLine("bus,");
block.AddLine("dbus::ObjectPath(object_path)),");
block.PopOffset();
// Signal constructors.
for (const auto& signal : interface.signals) {
block.AddLine(StringPrintf("signal_%s_(", signal.name.c_str()));
block.PushOffset(kLineContinuationOffset);
block.AddLine("&dbus_object_,");
block.AddLine(StringPrintf("\"%s\",", interface.name.c_str()));
block.AddLine(StringPrintf("\"%s\"),", signal.name.c_str()));
block.PopOffset();
}
block.AddLine("dbus_interface_(");
block.PushOffset(kLineContinuationOffset);
block.AddLine(StringPrintf(
"dbus_object_.AddOrGetInterface(\"%s\")) {", interface.name.c_str()));
block.PopOffset();
block.PopOffset();
block.PopOffset();
block.PushOffset(kBlockOffset);
for (const auto& method : interface.methods) {
if (method.output_arguments.size() > 1) {
// TODO(pstew): Accept multiple output arguments. crbug.com/419271
continue;
}
block.AddLine("dbus_interface_->AddMethodHandler(");
block.PushOffset(kLineContinuationOffset);
block.AddLine(StringPrintf("\"%s\",", method.name.c_str()));
block.AddLine("base::Unretained(interface_),");
block.AddLine(StringPrintf("&MethodInterface::%s);", method.name.c_str()));
block.PopOffset();
}
block.AddLine("dbus_object_.RegisterAsync(base::Bind(");
block.AddLineWithOffset(
StringPrintf("&%s::OnRegisterComplete, base::Unretained(this)));",
class_name.c_str()),
kLineContinuationOffset);
block.PopOffset();
block.AddLine("}");
text->AddBlock(block);
}
// static
void AdaptorGenerator::AddMethodInterface(const Interface& interface,
IndentedText *text) {
IndentedText block;
block.AddLine("class MethodInterface {");
block.AddLineWithOffset("public:", kScopeOffset);
DbusSignature signature;
block.PushOffset(kBlockOffset);
for (const auto& method : interface.methods) {
string return_type("void");
if (!method.output_arguments.empty()) {
if (method.output_arguments.size() > 1) {
// TODO(pstew): Accept multiple output arguments. crbug.com://419271
LOG(WARNING) << "Method " << method.name << " has "
<< method.output_arguments.size()
<< " output arguments which is unsupported.";
continue;
}
CHECK(signature.Parse(method.output_arguments[0].type, &return_type));
}
block.AddLine(StringPrintf("virtual %s %s(",
return_type.c_str(), method.name.c_str()));
block.PushOffset(kLineContinuationOffset);
string last_argument = "chromeos::ErrorPtr* /* error */";
for (const auto& argument : method.input_arguments) {
block.AddLine(last_argument + ",");
CHECK(signature.Parse(argument.type, &last_argument));
if (!IsIntegralType(last_argument)) {
last_argument = StringPrintf("const %s&", last_argument.c_str());
}
if (!argument.name.empty()) {
last_argument.append(StringPrintf(" /* %s */", argument.name.c_str()));
}
}
block.AddLine(last_argument + ") = 0;");
block.PopOffset();
}
block.PopOffset();
block.AddLine("};");
text->AddBlock(block);
}
// static
void AdaptorGenerator::AddSendSignalMethods(const Interface& interface,
IndentedText *text) {
IndentedText block;
DbusSignature signature;
for (const auto& signal : interface.signals) {
block.AddLine(StringPrintf("void Send%sSignal(", signal.name.c_str()));
block.PushOffset(kLineContinuationOffset);
string last_argument;
int unnamed_args = 0;
string call_arguments; // The arguments to pass to the Send() call.
for (const auto& argument : signal.arguments) {
if (!last_argument.empty())
block.AddLine(last_argument + ",");
CHECK(signature.Parse(argument.type, &last_argument));
if (!IsIntegralType(last_argument))
last_argument = StringPrintf("const %s&", last_argument.c_str());
string argument_name = argument.name;
if (argument.name.empty())
argument_name = StringPrintf("_arg_%d", ++unnamed_args);
last_argument.append(" " + argument_name);
if (!call_arguments.empty())
call_arguments.append(", ");
call_arguments.append(argument_name);
}
block.AddLine(last_argument + ") {");
block.PopOffset();
block.PushOffset(kBlockOffset);
block.AddLine(StringPrintf(
"signal_%s_.Send(%s);", signal.name.c_str(), call_arguments.c_str()));
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) {
block.AddLine("chromeos::dbus_utils::DBusSignal<");
block.PushOffset(kLineContinuationOffset);
string last_argument;
for (const auto& argument : signal.arguments) {
if (!last_argument.empty())
block.AddLine(last_argument + ",");
CHECK(signature.Parse(argument.type, &last_argument));
if (!argument.name.empty())
last_argument.append(StringPrintf(" /* %s */", argument.name.c_str()));
}
block.AddLine(StringPrintf(
"%s> signal_%s_;", last_argument.c_str(), signal.name.c_str()));
block.PopOffset();
}
text->AddBlock(block);
}
} // namespace chromeos_dbus_bindings