diff options
-rw-r--r-- | chromeos-dbus-bindings/adaptor_generator.cc | 70 | ||||
-rw-r--r-- | chromeos-dbus-bindings/adaptor_generator.h | 22 | ||||
-rw-r--r-- | chromeos-dbus-bindings/adaptor_generator_unittest.cc | 4 | ||||
-rw-r--r-- | chromeos-dbus-bindings/chromeos-dbus-bindings.gyp | 3 | ||||
-rw-r--r-- | chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc | 23 | ||||
-rw-r--r-- | chromeos-dbus-bindings/header_generator.cc | 74 | ||||
-rw-r--r-- | chromeos-dbus-bindings/header_generator.h | 52 | ||||
-rw-r--r-- | chromeos-dbus-bindings/method_name_generator.cc | 1 | ||||
-rw-r--r-- | chromeos-dbus-bindings/method_name_generator.h | 7 | ||||
-rw-r--r-- | chromeos-dbus-bindings/method_name_generator_unittest.cc | 3 | ||||
-rw-r--r-- | chromeos-dbus-bindings/proxy_generator.cc | 299 | ||||
-rw-r--r-- | chromeos-dbus-bindings/proxy_generator.h | 63 | ||||
-rw-r--r-- | chromeos-dbus-bindings/proxy_generator_unittest.cc | 248 |
13 files changed, 775 insertions, 94 deletions
diff --git a/chromeos-dbus-bindings/adaptor_generator.cc b/chromeos-dbus-bindings/adaptor_generator.cc index ea220c3..df77629 100644 --- a/chromeos-dbus-bindings/adaptor_generator.cc +++ b/chromeos-dbus-bindings/adaptor_generator.cc @@ -6,11 +6,7 @@ #include <string> -#include <base/file_util.h> -#include <base/files/file_path.h> #include <base/logging.h> -#include <base/strings/string_split.h> -#include <base/strings/string_util.h> #include <base/strings/stringprintf.h> #include "chromeos-dbus-bindings/dbus_signature.h" @@ -23,31 +19,26 @@ using std::vector; namespace chromeos_dbus_bindings { -namespace { -const int kScopeOffset = 1; -const int kBlockOffset = 2; -const int kLineContinuationOffset = 4; -} // namespace - +// 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.value(), - interface.name); + 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.AddLine(""); + 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.AddLine(""); + text.AddBlankLine(); vector<string> namespaces; string class_name; @@ -55,7 +46,7 @@ bool AdaptorGenerator::GenerateAdaptor( for (const auto& space : namespaces) { text.AddLine(StringPrintf("namespace %s {", space.c_str())); } - text.AddLine(""); + text.AddBlankLine(); string adaptor_name = StringPrintf("%sAdaptor", class_name.c_str()); text.AddLine(StringPrintf("class %s {", adaptor_name.c_str())); @@ -91,36 +82,14 @@ bool AdaptorGenerator::GenerateAdaptor( text.PopOffset(); text.AddLine("};"); - 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())); - string contents = text.GetContents(); - int expected_write_return = contents.size(); - if (base::WriteFile(output_file, contents.c_str(), contents.size()) != - expected_write_return) { - LOG(ERROR) << "Failed to write file " << output_file.value(); - return false; - } - return true; -} - -// static -string AdaptorGenerator::GenerateHeaderGuard( - const string& filename, const string& interface_name) { - string guard = StringPrintf("____chrome_dbus_binding___%s__%s", - interface_name.c_str(), filename.c_str()); - for (auto& c : guard) { - if (IsAsciiAlpha(c)) { - c = base::ToUpperASCII(c); - } else if (!IsAsciiDigit(c)) { - c = '_'; - } - } - return guard; + return WriteTextToFile(output_file, text); } // static @@ -214,27 +183,4 @@ void AdaptorGenerator::AddMethodInterface(const Interface& interface, text->AddBlock(block); } -// static -bool AdaptorGenerator::GetNamespacesAndClassName( - const string& interface_name, - vector<string>* namespaces, - string* class_name) { - vector<string> split_namespaces; - base::SplitString(interface_name, '.', &split_namespaces); - if (split_namespaces.size() < 2) { - LOG(ERROR) << "Interface name must have both a domain and object part " - << "separated by '.'. Got " << interface_name << " instead."; - return false; - } - *class_name = split_namespaces.back(); - split_namespaces.pop_back(); - namespaces->swap(split_namespaces); - return true; -} - -// static -bool AdaptorGenerator::IsIntegralType(const string& type) { - return type.find("::") == std::string::npos; -} - } // namespace chromeos_dbus_bindings diff --git a/chromeos-dbus-bindings/adaptor_generator.h b/chromeos-dbus-bindings/adaptor_generator.h index 250557c..fe32409 100644 --- a/chromeos-dbus-bindings/adaptor_generator.h +++ b/chromeos-dbus-bindings/adaptor_generator.h @@ -10,6 +10,7 @@ #include <base/macros.h> +#include "chromeos-dbus-bindings/header_generator.h" #include "chromeos-dbus-bindings/indented_text.h" namespace base { @@ -23,21 +24,14 @@ namespace chromeos_dbus_bindings { class IndentedText; struct Interface; -class AdaptorGenerator { +class AdaptorGenerator : public HeaderGenerator { public: - AdaptorGenerator() = default; - virtual ~AdaptorGenerator() = default; - - bool GenerateAdaptor(const Interface& interface, - const base::FilePath& output_file); + static bool GenerateAdaptor(const Interface& interface, + const base::FilePath& output_file); private: friend class AdaptorGeneratorTest; - // Create a unique header guard string to protect multiple includes of header. - static std::string GenerateHeaderGuard(const std::string& filename, - const std::string& interface_name); - // Generates the constructor for the adaptor. static void AddConstructor(const Interface& interface, const std::string& class_name, @@ -47,14 +41,6 @@ class AdaptorGenerator { static void AddMethodInterface(const Interface& interface, IndentedText *text); - // Returns a vector of nesting namepsaces. - static bool GetNamespacesAndClassName(const std::string& interface_name, - std::vector<std::string>* namespaces, - std::string* class_name); - - // Used to decide whether the argument should be a const reference. - static bool IsIntegralType(const std::string& type); - DISALLOW_COPY_AND_ASSIGN(AdaptorGenerator); }; diff --git a/chromeos-dbus-bindings/adaptor_generator_unittest.cc b/chromeos-dbus-bindings/adaptor_generator_unittest.cc index edd1e3a..58e0863 100644 --- a/chromeos-dbus-bindings/adaptor_generator_unittest.cc +++ b/chromeos-dbus-bindings/adaptor_generator_unittest.cc @@ -42,6 +42,7 @@ const char kExpectedContent[] = R"literal_string( #include <base/macros.h> #include <dbus/object_path.h> +#include <chromeos/any.h> #include <chromeos/dbus/dbus_object.h> #include <chromeos/dbus/exported_object_manager.h> #include <chromeos/variant_dictionary.h> @@ -126,7 +127,6 @@ class AdaptorGeneratorTest : public Test { } base::ScopedTempDir temp_dir_; - AdaptorGenerator generator_; }; TEST_F(AdaptorGeneratorTest, GenerateAdaptors) { @@ -151,7 +151,7 @@ TEST_F(AdaptorGeneratorTest, GenerateAdaptors) { {"", kMethod3Return0}, {"", kMethod3Return1}}); base::FilePath output_path = temp_dir_.path().Append("output.h"); - EXPECT_TRUE(generator_.GenerateAdaptor(interface, output_path)); + EXPECT_TRUE(AdaptorGenerator::GenerateAdaptor(interface, output_path)); string contents; EXPECT_TRUE(base::ReadFileToString(output_path, &contents)); // The header guards contain the (temporary) filename, so we search for diff --git a/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp b/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp index 1d92c87..5b729d4 100644 --- a/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp +++ b/chromeos-dbus-bindings/chromeos-dbus-bindings.gyp @@ -26,8 +26,10 @@ 'sources': [ 'adaptor_generator.cc', 'dbus_signature.cc', + 'header_generator.cc', 'indented_text.cc', 'method_name_generator.cc', + 'proxy_generator.cc', 'xml_interface_parser.cc', ], 'variables': { @@ -77,6 +79,7 @@ 'dbus_signature_unittest.cc', 'indented_text_unittest.cc', 'method_name_generator_unittest.cc', + 'proxy_generator_unittest.cc', 'xml_interface_parser_unittest.cc', ], }, diff --git a/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc b/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc index 7da9faf..5bf35e1 100644 --- a/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc +++ b/chromeos-dbus-bindings/generate_chromeos_dbus_bindings.cc @@ -11,6 +11,7 @@ #include "chromeos-dbus-bindings/adaptor_generator.h" #include "chromeos-dbus-bindings/method_name_generator.h" +#include "chromeos-dbus-bindings/proxy_generator.h" #include "chromeos-dbus-bindings/xml_interface_parser.h" namespace switches { @@ -19,6 +20,7 @@ static const char kHelp[] = "help"; static const char kInput[] = "input"; static const char kMethodNames[] = "method-names"; static const char kAdaptor[] = "adaptor"; +static const char kProxy[] = "proxy"; static const char kHelpMessage[] = "\n" "Available Switches: \n" " --input=<interface>\n" @@ -26,7 +28,9 @@ static const char kHelpMessage[] = "\n" " --method-names=<method name header filename>\n" " The output header file with string constants for each method name.\n" " --adaptor=<adaptor header filename>\n" - " The output header file with DBus adaptor class.\n"; + " The output header file name containing the DBus adaptor class.\n" + " --proxy=<proxy header filename>\n" + " The output header file name containing the DBus proxy class.\n"; } // namespace switches @@ -61,8 +65,7 @@ int main(int argc, char** argv) { std::string method_name_file = cl->GetSwitchValueASCII(switches::kMethodNames); VLOG(1) << "Outputting method names to " << method_name_file; - chromeos_dbus_bindings::MethodNameGenerator method_name_generator; - if (!method_name_generator.GenerateMethodNames( + if (!chromeos_dbus_bindings::MethodNameGenerator::GenerateMethodNames( parser.interface(), base::FilePath(method_name_file))) { LOG(ERROR) << "Failed to output method names."; @@ -73,8 +76,7 @@ int main(int argc, char** argv) { if (cl->HasSwitch(switches::kAdaptor)) { std::string adaptor_file = cl->GetSwitchValueASCII(switches::kAdaptor); VLOG(1) << "Outputting adaptor to " << adaptor_file; - chromeos_dbus_bindings::AdaptorGenerator adaptor_generator; - if (!adaptor_generator.GenerateAdaptor( + if (!chromeos_dbus_bindings::AdaptorGenerator::GenerateAdaptor( parser.interface(), base::FilePath(adaptor_file))) { LOG(ERROR) << "Failed to output adaptor."; @@ -82,5 +84,16 @@ int main(int argc, char** argv) { } } + if (cl->HasSwitch(switches::kProxy)) { + std::string proxy_file = cl->GetSwitchValueASCII(switches::kProxy); + LOG(INFO) << "Outputting proxy to " << proxy_file; + if (!chromeos_dbus_bindings::ProxyGenerator::GenerateProxy( + parser.interface(), + base::FilePath(proxy_file))) { + LOG(ERROR) << "Failed to output proxy."; + return 1; + } + } + return 0; } diff --git a/chromeos-dbus-bindings/header_generator.cc b/chromeos-dbus-bindings/header_generator.cc new file mode 100644 index 0000000..c339c13 --- /dev/null +++ b/chromeos-dbus-bindings/header_generator.cc @@ -0,0 +1,74 @@ +// 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/header_generator.h" + +#include <string> + +#include <base/file_util.h> +#include <base/files/file_path.h> +#include <base/strings/string_split.h> +#include <base/strings/string_util.h> +#include <base/strings/stringprintf.h> + +#include "chromeos-dbus-bindings/indented_text.h" + +using std::string; +using std::vector; + +namespace chromeos_dbus_bindings { + +// static +string HeaderGenerator::GenerateHeaderGuard( + const base::FilePath& output_file, const string& interface_name) { + string guard = base::StringPrintf("____chromeos_dbus_binding___%s__%s", + interface_name.c_str(), + output_file.value().c_str()); + for (auto& c : guard) { + if (IsAsciiAlpha(c)) { + c = base::ToUpperASCII(c); + } else if (!IsAsciiDigit(c)) { + c = '_'; + } + } + return guard; +} + +// static +bool HeaderGenerator::GetNamespacesAndClassName( + const string& interface_name, + vector<string>* namespaces, + string* class_name) { + vector<string> split_namespaces; + base::SplitString(interface_name, '.', &split_namespaces); + if (split_namespaces.size() < 2) { + LOG(ERROR) << "Interface name must have both a domain and object part " + << "separated by '.'. Got " << interface_name << " instead."; + return false; + } + *class_name = split_namespaces.back(); + split_namespaces.pop_back(); + namespaces->swap(split_namespaces); + return true; +} + +// static +bool HeaderGenerator::IsIntegralType(const string& type) { + return type.find("::") == std::string::npos; +} + +// static +bool HeaderGenerator::WriteTextToFile( + const base::FilePath& output_file, const IndentedText &text) { + string contents = text.GetContents(); + int expected_write_return = contents.size(); + if (base::WriteFile(output_file, contents.c_str(), contents.size()) != + expected_write_return) { + LOG(ERROR) << "Failed to write file " << output_file.value(); + return false; + } + return true; +} + +} // namespace chromeos_dbus_bindings diff --git a/chromeos-dbus-bindings/header_generator.h b/chromeos-dbus-bindings/header_generator.h new file mode 100644 index 0000000..7be8d5b --- /dev/null +++ b/chromeos-dbus-bindings/header_generator.h @@ -0,0 +1,52 @@ +// 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. + +#ifndef CHROMEOS_DBUS_BINDINGS_HEADER_GENERATOR_H_ +#define CHROMEOS_DBUS_BINDINGS_HEADER_GENERATOR_H_ + +#include <string> +#include <vector> + +#include <base/macros.h> + +namespace base { + +class FilePath; + +}; + +namespace chromeos_dbus_bindings { + +struct Interface; +class IndentedText; + +class HeaderGenerator { + protected: + // Create a unique header guard string to protect multiple includes of header. + static std::string GenerateHeaderGuard(const base::FilePath& output_file, + const std::string& interface_name); + + // Returns a vector of nesting namepsaces. + static bool GetNamespacesAndClassName(const std::string& interface_name, + std::vector<std::string>* namespaces, + std::string* class_name); + + // Used to decide whether the argument should be a const reference. + static bool IsIntegralType(const std::string& type); + + // Writes indented text to a file. + static bool WriteTextToFile(const base::FilePath& output_file, + const IndentedText& text); + + static const int kScopeOffset = 1; + static const int kBlockOffset = 2; + static const int kLineContinuationOffset = 4; + + private: + DISALLOW_COPY_AND_ASSIGN(HeaderGenerator); +}; + +} // namespace chromeos_dbus_bindings + +#endif // CHROMEOS_DBUS_BINDINGS_HEADER_GENERATOR_H_ diff --git a/chromeos-dbus-bindings/method_name_generator.cc b/chromeos-dbus-bindings/method_name_generator.cc index b2d49f4..4b4154e 100644 --- a/chromeos-dbus-bindings/method_name_generator.cc +++ b/chromeos-dbus-bindings/method_name_generator.cc @@ -23,6 +23,7 @@ string MethodNameGenerator::GenerateMethodNameConstant( return "k" + method_name + "Method"; } +// static bool MethodNameGenerator::GenerateMethodNames( const Interface& interface, const base::FilePath& output_file) { diff --git a/chromeos-dbus-bindings/method_name_generator.h b/chromeos-dbus-bindings/method_name_generator.h index af2576f..59ca890 100644 --- a/chromeos-dbus-bindings/method_name_generator.h +++ b/chromeos-dbus-bindings/method_name_generator.h @@ -21,11 +21,8 @@ struct Interface; class MethodNameGenerator { public: - MethodNameGenerator() = default; - virtual ~MethodNameGenerator() = default; - - virtual bool GenerateMethodNames(const Interface &interface, - const base::FilePath& output_file); + static bool GenerateMethodNames(const Interface &interface, + const base::FilePath& output_file); static std::string GenerateMethodNameConstant(const std::string& method_name); private: diff --git a/chromeos-dbus-bindings/method_name_generator_unittest.cc b/chromeos-dbus-bindings/method_name_generator_unittest.cc index e73b8ba..dc461d6 100644 --- a/chromeos-dbus-bindings/method_name_generator_unittest.cc +++ b/chromeos-dbus-bindings/method_name_generator_unittest.cc @@ -45,7 +45,6 @@ class MethodNameGeneratorTest : public Test { } base::ScopedTempDir temp_dir_; - MethodNameGenerator generator_; }; TEST_F(MethodNameGeneratorTest, GnerateMethodNames) { @@ -54,7 +53,7 @@ TEST_F(MethodNameGeneratorTest, GnerateMethodNames) { interface.methods.emplace_back(kMethodName1); interface.methods.emplace_back(kMethodName2); base::FilePath output_path = temp_dir_.path().Append("output.h"); - EXPECT_TRUE(generator_.GenerateMethodNames(interface, output_path)); + EXPECT_TRUE(MethodNameGenerator::GenerateMethodNames(interface, output_path)); string contents; EXPECT_TRUE(base::ReadFileToString(output_path, &contents)); EXPECT_STREQ(kExpectedOutput, contents.c_str()); diff --git a/chromeos-dbus-bindings/proxy_generator.cc b/chromeos-dbus-bindings/proxy_generator.cc new file mode 100644 index 0000000..34b44c0 --- /dev/null +++ b/chromeos-dbus-bindings/proxy_generator.cc @@ -0,0 +1,299 @@ +// 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 diff --git a/chromeos-dbus-bindings/proxy_generator.h b/chromeos-dbus-bindings/proxy_generator.h new file mode 100644 index 0000000..66dd8ba --- /dev/null +++ b/chromeos-dbus-bindings/proxy_generator.h @@ -0,0 +1,63 @@ +// 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. + +#ifndef CHROMEOS_DBUS_BINDINGS_PROXY_GENERATOR_H_ +#define CHROMEOS_DBUS_BINDINGS_PROXY_GENERATOR_H_ + +#include <string> +#include <vector> + +#include <base/macros.h> + +#include "chromeos-dbus-bindings/header_generator.h" +#include "chromeos-dbus-bindings/indented_text.h" +#include "chromeos-dbus-bindings/interface.h" + +namespace base { + +class FilePath; + +} // namespace base + +namespace chromeos_dbus_bindings { + +class IndentedText; +struct Interface; + +class ProxyGenerator : public HeaderGenerator { + public: + static bool GenerateProxy(const Interface& interface, + const base::FilePath& output_file); + + private: + friend class ProxyGeneratorTest; + + // Generates the constructor and destructor for the proxy. + static void AddConstructor(const Interface& interface, + const std::string& class_name, + IndentedText* text); + static void AddDestructor(const std::string& class_name, + IndentedText* text); + + // Generates a callback for signal receiver registration completion. + static void AddSignalConnectedCallback(IndentedText *text); + + // Generates the method signatures for signal receivers. + static void AddMethodInterface(const Interface& interface, + IndentedText* text); + + // Generates a native C++ method which calls a D-Bus method on the proxy. + static void AddMethodProxy(const Interface::Method& interface, + const std::string& interface_name, + IndentedText* text); + + // Generates the signal handler name for a given signal name. + static std::string GetHandlerNameForSignal(const std::string& signal); + + DISALLOW_COPY_AND_ASSIGN(ProxyGenerator); +}; + +} // namespace chromeos_dbus_bindings + +#endif // CHROMEOS_DBUS_BINDINGS_PROXY_GENERATOR_H_ diff --git a/chromeos-dbus-bindings/proxy_generator_unittest.cc b/chromeos-dbus-bindings/proxy_generator_unittest.cc new file mode 100644 index 0000000..1149de9 --- /dev/null +++ b/chromeos-dbus-bindings/proxy_generator_unittest.cc @@ -0,0 +1,248 @@ +// 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 <string> +#include <vector> + +#include <base/file_util.h> +#include <base/files/file_path.h> +#include <base/files/scoped_temp_dir.h> +#include <gtest/gtest.h> + +#include "chromeos-dbus-bindings/interface.h" + +using std::string; +using std::vector; +using testing::Test; + +namespace chromeos_dbus_bindings { + +namespace { + +const char kInterfaceName[] = "org.chromium.TestInterface"; +const char kMethod1Name[] = "Elements"; +const char kMethod1Return[] = "s"; +const char kMethod1Argument1[] = "s"; +const char kMethod1ArgumentName1[] = "space_walk"; +const char kMethod1Argument2[] = "ao"; +const char kMethod1ArgumentName2[] = "ramblin_man"; +const char kMethod2Name[] = "ReturnToPatagonia"; +const char kMethod2Return[] = "x"; +const char kMethod3Name[] = "NiceWeatherForDucks"; +const char kMethod3Argument1[] = "b"; +const char kMethod4Name[] = "ExperimentNumberSix"; +const char kSignal1Name[] = "Closer"; +const char kSignal2Name[] = "TheCurseOfKaZar"; +const char kSignal2Argument1[] = "as"; +const char kSignal2Argument2[] = "y"; +const char kExpectedContent[] = R"literal_string( +#include <string> +#include <vector> + +#include <base/bind.h> +#include <base/callback.h> +#include <base/logging.h> +#include <base/macros.h> +#include <base/memory/ref_counted.h> +#include <chromeos/any.h> +#include <chromeos/dbus/dbus_method_invoker.h> +#include <chromeos/dbus/dbus_signal_handler.h> +#include <chromeos/errors/error.h> +#include <dbus/bus.h> +#include <dbus/message.h> +#include <dbus/object_path.h> +#include <dbus/object_proxy.h> + +namespace org { +namespace chromium { + +class TestInterfaceProxy { + public: + class SignalReceiver { + public: + virtual void OnCloserSignal() {} + virtual void OnTheCurseOfKaZarSignal( + const std::vector<std::string>&, + uint8_t) {} + }; + TestInterfaceProxy( + const scoped_refptr<dbus::Bus>& bus, + const std::string& service_name, + const std::string& object_path, + SignalReceiver* signal_receiver) + : bus_(bus), + service_name_(service_name), + object_path_(object_path), + dbus_object_proxy_( + bus_->GetObjectProxy(service_name_, object_path_)) { + chromeos::dbus_utils::ConnectToSignal( + dbus_object_proxy_, + "org.chromium.TestInterface", + "Closer", + base::Bind( + &SignalReceiver::OnCloserSignal, + base::Unretained(signal_receiver)), + base::Bind( + &TestInterfaceProxy::OnDBusSignalConnected, + base::Unretained(this))); + chromeos::dbus_utils::ConnectToSignal( + dbus_object_proxy_, + "org.chromium.TestInterface", + "TheCurseOfKaZar", + base::Bind( + &SignalReceiver::OnTheCurseOfKaZarSignal, + base::Unretained(signal_receiver)), + base::Bind( + &TestInterfaceProxy::OnDBusSignalConnected, + base::Unretained(this))); + } + virtual ~TestInterfaceProxy() { + dbus_object_proxy_->Detach(); + bus_->RemoveObjectProxy(service_name_, object_path_, base::Closure()); + } + void OnDBusSignalConnected( + const std::string& interface, + const std::string& signal, + bool success) { + if (!success) { + LOG(ERROR) + << "Failed to connect to " << interface << "." << signal + << " for " << service_name_ << " at " + << object_path_.value(); + } + } + virtual std::string Elements( + const std::string& space_walk_in, + const std::vector<dbus::ObjectPath>& ramblin_man_in, + chromeos::ErrorPtr* error) { + auto response = chromeos::dbus_utils::CallMethodAndBlock( + dbus_object_proxy_, + "org.chromium.TestInterface", + "Elements", + error, + space_walk_in, + ramblin_man_in); + std::string result{}; + if (!response) { + return result; + } + chromeos::dbus_utils::ExtractMethodCallResults( + response.get(), error, &result); + return result; + } + virtual int64_t ReturnToPatagonia( + chromeos::ErrorPtr* error) { + auto response = chromeos::dbus_utils::CallMethodAndBlock( + dbus_object_proxy_, + "org.chromium.TestInterface", + "ReturnToPatagonia", + error); + int64_t result{}; + if (!response) { + return result; + } + chromeos::dbus_utils::ExtractMethodCallResults( + response.get(), error, &result); + return result; + } + virtual void NiceWeatherForDucks( + bool argument1_in, + chromeos::ErrorPtr* error) { + auto response = chromeos::dbus_utils::CallMethodAndBlock( + dbus_object_proxy_, + "org.chromium.TestInterface", + "NiceWeatherForDucks", + error, + argument1_in); + if (!response) { + return; + } + chromeos::dbus_utils::ExtractMethodCallResults( + response.get(), error); + } + virtual void ExperimentNumberSix( + chromeos::ErrorPtr* error) { + auto response = chromeos::dbus_utils::CallMethodAndBlock( + dbus_object_proxy_, + "org.chromium.TestInterface", + "ExperimentNumberSix", + error); + if (!response) { + return; + } + chromeos::dbus_utils::ExtractMethodCallResults( + response.get(), error); + } + + private: + scoped_refptr<dbus::Bus> bus_; + std::string service_name_; + dbus::ObjectPath object_path_; + dbus::ObjectProxy* dbus_object_proxy_; + + DISALLOW_COPY_AND_ASSIGN(TestInterfaceProxy); +}; + +} // namespace chromium +} // namespace org +)literal_string"; + +} // namespace + +class ProxyGeneratorTest : public Test { + public: + void SetUp() override { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + } + + protected: + base::FilePath CreateInputFile(const string& contents) { + base::FilePath path; + EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &path)); + EXPECT_EQ(contents.size(), + base::WriteFile(path, contents.c_str(), contents.size())); + return path; + } + + base::ScopedTempDir temp_dir_; +}; + +TEST_F(ProxyGeneratorTest, GenerateAdaptors) { + Interface interface; + interface.name = kInterfaceName; + interface.methods.emplace_back( + kMethod1Name, + vector<Interface::Argument>{ + {kMethod1ArgumentName1, kMethod1Argument1}, + {kMethod1ArgumentName2, kMethod1Argument2}}, + vector<Interface::Argument>{{"", kMethod1Return}}); + interface.methods.emplace_back( + kMethod2Name, + vector<Interface::Argument>{}, + vector<Interface::Argument>{{"", kMethod2Return}}); + interface.methods.emplace_back( + kMethod3Name, + vector<Interface::Argument>{{"", kMethod3Argument1}}, + vector<Interface::Argument>{}); + interface.methods.emplace_back(kMethod4Name); + interface.signals.emplace_back(kSignal1Name); + interface.signals.emplace_back( + kSignal2Name, + vector<Interface::Argument>{ + {"", kSignal2Argument1}, + {"", kSignal2Argument2}}); + base::FilePath output_path = temp_dir_.path().Append("output.h"); + EXPECT_TRUE(ProxyGenerator::GenerateProxy(interface, output_path)); + string contents; + EXPECT_TRUE(base::ReadFileToString(output_path, &contents)); + // The header guards contain the (temporary) filename, so we search for + // the content we need within the string. + EXPECT_NE(string::npos, contents.find(kExpectedContent)) + << "Expected to find the following content...\n" + << kExpectedContent << "...within content...\n" << contents; +} + +} // namespace chromeos_dbus_bindings |