blob: 34b44c03ad10ae7a0bf5da4072a5d8e90275da47 [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/proxy_generator.h"
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include "chromeos-dbus-bindings/dbus_signature.h"
#include "chromeos-dbus-bindings/indented_text.h"
using base::StringPrintf;
using std::string;
using std::vector;
namespace chromeos_dbus_bindings {
// static
bool ProxyGenerator::GenerateProxy(
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/bind.h>");
text.AddLine("#include <base/callback.h>");
text.AddLine("#include <base/logging.h>");
text.AddLine("#include <base/macros.h>");
text.AddLine("#include <base/memory/ref_counted.h>");
text.AddLine("#include <chromeos/any.h>");
text.AddLine("#include <chromeos/dbus/dbus_method_invoker.h>");
text.AddLine("#include <chromeos/dbus/dbus_signal_handler.h>");
text.AddLine("#include <chromeos/errors/error.h>");
text.AddLine("#include <dbus/bus.h>");
text.AddLine("#include <dbus/message.h>");
text.AddLine("#include <dbus/object_path.h>");
text.AddLine("#include <dbus/object_proxy.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 proxy_name = StringPrintf("%sProxy", class_name.c_str());
text.AddLine(StringPrintf("class %s {", proxy_name.c_str()));
text.AddLineWithOffset("public:", kScopeOffset);
text.PushOffset(kBlockOffset);
AddMethodInterface(interface, &text);
AddConstructor(interface, proxy_name, &text);
AddDestructor(proxy_name, &text);
AddSignalConnectedCallback(&text);
for (const auto& method : interface.methods) {
AddMethodProxy(method, interface.name, &text);
}
text.PopOffset();
text.AddBlankLine();
text.AddLineWithOffset("private:", kScopeOffset);
text.PushOffset(kBlockOffset);
text.AddLine("scoped_refptr<dbus::Bus> bus_;");
text.AddLine("std::string service_name_;");
text.AddLine("dbus::ObjectPath object_path_;");
text.AddLine("dbus::ObjectProxy* dbus_object_proxy_;");
text.AddBlankLine();
text.AddLine(StringPrintf(
"DISALLOW_COPY_AND_ASSIGN(%s);", proxy_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 ProxyGenerator::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("const scoped_refptr<dbus::Bus>& bus,");
block.AddLine("const std::string& service_name,");
block.AddLine("const std::string& object_path,");
block.AddLine("SignalReceiver* signal_receiver)");
block.AddLine(": bus_(bus),");
block.PushOffset(kBlockOffset);
block.AddLine("service_name_(service_name),");
block.AddLine("object_path_(object_path),");
block.AddLine("dbus_object_proxy_(");
block.AddLineWithOffset(
"bus_->GetObjectProxy(service_name_, object_path_)) {",
kLineContinuationOffset);
block.PopOffset();
block.PopOffset();
block.PushOffset(kBlockOffset);
for (const auto& signal : interface.signals) {
block.AddLine("chromeos::dbus_utils::ConnectToSignal(");
block.PushOffset(kLineContinuationOffset);
block.AddLine("dbus_object_proxy_,");
block.AddLine(StringPrintf("\"%s\",", interface.name.c_str()));
block.AddLine(StringPrintf("\"%s\",", signal.name.c_str()));
block.AddLine("base::Bind(");
block.PushOffset(kLineContinuationOffset);
block.AddLine(StringPrintf(
"&SignalReceiver::%s,",
GetHandlerNameForSignal(signal.name).c_str()));
block.AddLine("base::Unretained(signal_receiver)),");
block.PopOffset();
block.AddLine("base::Bind(");
block.PushOffset(kLineContinuationOffset);
block.AddLine(StringPrintf(
"&%s::OnDBusSignalConnected,", class_name.c_str()));
block.AddLine("base::Unretained(this)));");
block.PopOffset();
block.PopOffset();
}
block.PopOffset();
block.AddLine("}");
text->AddBlock(block);
}
// static
void ProxyGenerator::AddDestructor(const string& class_name,
IndentedText* text) {
IndentedText block;
block.AddLine(StringPrintf("virtual ~%s() {", class_name.c_str()));
block.PushOffset(kBlockOffset);
block.AddLine("dbus_object_proxy_->Detach();");
block.AddLine(
"bus_->RemoveObjectProxy(service_name_, object_path_, base::Closure());");
block.PopOffset();
block.AddLine("}");
text->AddBlock(block);
}
// static
void ProxyGenerator::AddSignalConnectedCallback(IndentedText* text) {
IndentedText block;
block.AddLine("void OnDBusSignalConnected(");
block.PushOffset(kLineContinuationOffset);
block.AddLine("const std::string& interface,");
block.AddLine("const std::string& signal,");
block.AddLine("bool success) {");
block.PopOffset();
block.PushOffset(kBlockOffset);
block.AddLine("if (!success) {");
block.PushOffset(kBlockOffset);
block.AddLine("LOG(ERROR)");
block.PushOffset(kLineContinuationOffset);
block.AddLine("<< \"Failed to connect to \" << interface << \".\" << signal");
block.AddLine("<< \" for \" << service_name_ << \" at \"");
block.AddLine("<< object_path_.value();");
block.PopOffset();
block.PopOffset();
block.AddLine("}");
block.PopOffset();
block.AddLine("}");
text->AddBlock(block);
}
// static
void ProxyGenerator::AddMethodInterface(const Interface& interface,
IndentedText* text) {
IndentedText block;
block.AddLine("class SignalReceiver {");
block.AddLineWithOffset("public:", kScopeOffset);
block.PushOffset(kBlockOffset);
DbusSignature signature;
for (const auto& signal : interface.signals) {
if (signal.arguments.empty()) {
block.AddLine(StringPrintf("virtual void %s() {}",
GetHandlerNameForSignal(signal.name).c_str()));
continue;
}
block.AddLine(StringPrintf("virtual void %s(",
GetHandlerNameForSignal(signal.name).c_str()));
block.PushOffset(kLineContinuationOffset);
string last_argument;
vector<string> argument_types;
for (const auto& argument : signal.arguments) {
if (!last_argument.empty()) {
block.AddLine(StringPrintf("%s,", last_argument.c_str()));
}
CHECK(signature.Parse(argument.type, &last_argument));
if (!IsIntegralType(last_argument)) {
last_argument = StringPrintf("const %s&", last_argument.c_str());
}
}
block.AddLine(StringPrintf("%s) {}", last_argument.c_str()));
block.PopOffset();
}
block.PopOffset();
block.AddLine("};");
text->AddBlock(block);
}
// static
void ProxyGenerator::AddMethodProxy(const Interface::Method& method,
const string& interface_name,
IndentedText* text) {
IndentedText block;
string return_type("void");
bool is_void_method = true;
DbusSignature signature;
if (!method.output_arguments.empty()) {
if (method.output_arguments.size() > 1) {
LOG(WARNING) << "Method " << method.name << " has "
<< method.output_arguments.size()
<< " output arguments which is unsupported.";
return;
}
CHECK(signature.Parse(method.output_arguments[0].type, &return_type));
is_void_method = false;
}
block.AddLine(StringPrintf("virtual %s %s(",
return_type.c_str(), method.name.c_str()));
block.PushOffset(kLineContinuationOffset);
vector<string> argument_names;
int argument_number = 0;
for (const auto& argument : method.input_arguments) {
string argument_type;
CHECK(signature.Parse(argument.type, &argument_type));
if (!IsIntegralType(argument_type)) {
argument_type = StringPrintf("const %s&", argument_type.c_str());
}
++argument_number;
string argument_prefix(argument.name.empty() ?
StringPrintf("argument%d", argument_number) :
argument.name);
string argument_name(StringPrintf("%s_in", argument_prefix.c_str()));
argument_names.push_back(argument_name);
block.AddLine(StringPrintf(
"%s %s,", argument_type.c_str(), argument_name.c_str()));
}
block.AddLine("chromeos::ErrorPtr* error) {");
block.PopOffset();
block.PushOffset(kBlockOffset);
block.AddLine("auto response = chromeos::dbus_utils::CallMethodAndBlock(");
block.PushOffset(kLineContinuationOffset);
block.AddLine("dbus_object_proxy_,");
block.AddLine(StringPrintf("\"%s\",", interface_name.c_str()));
block.AddLine(StringPrintf("\"%s\",", method.name.c_str()));
string last_argument = "error";
for (const auto& argument_name : argument_names) {
block.AddLine(StringPrintf("%s,", last_argument.c_str()));
last_argument = argument_name;
}
block.AddLine(StringPrintf("%s);", last_argument.c_str()));
block.PopOffset();
if (!is_void_method) {
block.AddLine(StringPrintf("%s result{};", return_type.c_str()));
}
block.AddLine("if (!response) {");
block.AddLineWithOffset(StringPrintf(
"return%s;", is_void_method ? "" : " result"), kBlockOffset);
block.AddLine("}");
block.AddLine("chromeos::dbus_utils::ExtractMethodCallResults(");
block.AddLineWithOffset(StringPrintf("response.get(), error%s);",
is_void_method ? "" : ", &result"),
kLineContinuationOffset);
if (!is_void_method) {
block.AddLine("return result;");
}
block.PopOffset();
block.AddLine("}");
text->AddBlock(block);
}
// static
string ProxyGenerator::GetHandlerNameForSignal(const string& signal) {
return StringPrintf("On%sSignal", signal.c_str());
}
} // namespace chromeos_dbus_bindings