| // 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 <algorithm> |
| #include <utility> |
| |
| #include <base/check.h> |
| #include <base/files/file_path.h> |
| #include <base/format_macros.h> |
| #include <base/logging.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| |
| #include "chromeos-dbus-bindings/dbus_signature.h" |
| #include "chromeos-dbus-bindings/disallow_copy_and_assign.h" |
| #include "chromeos-dbus-bindings/header_generator.h" |
| #include "chromeos-dbus-bindings/indented_text.h" |
| #include "chromeos-dbus-bindings/name_parser.h" |
| |
| using base::StringPrintf; |
| using std::pair; |
| using std::string; |
| using std::vector; |
| |
| namespace chromeos_dbus_bindings { |
| |
| namespace { |
| // Helper struct to encapsulate information about method call parameter during |
| // code generation. |
| struct ParamDef { |
| ParamDef(const string& param_type, const string& param_name, bool param_ref) |
| : type(param_type), name(param_name), is_const_ref(param_ref) {} |
| |
| string type; |
| string name; |
| bool is_const_ref; |
| }; |
| |
| string GetParamString(const ParamDef& param_def) { |
| return StringPrintf(param_def.is_const_ref ? "const %s& %s" : "%s* %s", |
| param_def.type.c_str(), param_def.name.c_str()); |
| } |
| } // anonymous namespace |
| |
| ProxyGenerator::ProxyGenerator() = default; |
| |
| bool ProxyGenerator::GenerateProxies(const ServiceConfig& config, |
| const std::vector<Interface>& interfaces, |
| const base::FilePath& output_file) { |
| IndentedText text; |
| |
| 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 <vector>"); |
| text.AddBlankLine(); |
| text.AddLine("#include <base/bind.h>"); |
| text.AddLine("#include <base/callback.h>"); |
| text.AddLine("#include <base/files/scoped_file.h>"); |
| text.AddLine("#include <base/logging.h>"); |
| text.AddLine("#include <base/macros.h>"); |
| text.AddLine("#include <base/memory/ref_counted.h>"); |
| text.AddLine("#include <brillo/any.h>"); |
| text.AddLine("#include <brillo/dbus/dbus_method_invoker.h>"); |
| text.AddLine("#include <brillo/dbus/dbus_property.h>"); |
| text.AddLine("#include <brillo/dbus/dbus_signal_handler.h>"); |
| text.AddLine("#include <brillo/dbus/file_descriptor.h>"); |
| text.AddLine("#include <brillo/errors/error.h>"); |
| text.AddLine("#include <brillo/variant_dictionary.h>"); |
| text.AddLine("#include <dbus/bus.h>"); |
| text.AddLine("#include <dbus/message.h>"); |
| text.AddLine("#include <dbus/object_manager.h>"); |
| text.AddLine("#include <dbus/object_path.h>"); |
| text.AddLine("#include <dbus/object_proxy.h>"); |
| text.AddBlankLine(); |
| |
| if (!config.object_manager.name.empty()) { |
| // Add forward-declaration for Object Manager proxy class. |
| NameParser parser{config.object_manager.name}; |
| parser.AddOpenNamespaces(&text, false); |
| text.AddLine( |
| StringPrintf("class %s;", parser.MakeProxyName(false).c_str())); |
| parser.AddCloseNamespaces(&text, false); |
| text.AddBlankLine(); |
| } |
| |
| for (const auto& interface : interfaces) { |
| GenerateInterfaceProxyInterface(config, interface, &text); |
| GenerateInterfaceProxy(config, interface, &text); |
| } |
| |
| ObjectManager::GenerateProxy(config, interfaces, &text); |
| |
| text.AddLine(StringPrintf("#endif // %s", header_guard.c_str())); |
| return WriteTextToFile(output_file, text); |
| } |
| |
| bool ProxyGenerator::GenerateMocks(const ServiceConfig& config, |
| const std::vector<Interface>& interfaces, |
| const base::FilePath& mock_file, |
| const base::FilePath& proxy_file, |
| bool use_literal_proxy_file) { |
| IndentedText text; |
| |
| text.AddLine("// Automatic generation of D-Bus interface mock proxies for:"); |
| for (const auto& interface : interfaces) { |
| text.AddLine(StringPrintf("// - %s", interface.name.c_str())); |
| } |
| string header_guard = GenerateHeaderGuard(mock_file); |
| 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/callback_forward.h>"); |
| text.AddLine("#include <base/logging.h>"); |
| text.AddLine("#include <base/macros.h>"); |
| text.AddLine("#include <brillo/any.h>"); |
| text.AddLine("#include <brillo/errors/error.h>"); |
| text.AddLine("#include <brillo/variant_dictionary.h>"); |
| text.AddLine("#include <gmock/gmock.h>"); |
| text.AddBlankLine(); |
| |
| if (!proxy_file.empty()) { |
| // If we have a proxy header file, it would have the proxy interfaces we |
| // need to base our mocks on, so we need to include that header file. |
| base::FilePath relative_path; |
| if (use_literal_proxy_file) { |
| relative_path = proxy_file; |
| } else { |
| // Generate a relative path from |mock_file| to |proxy_file|. |
| // First, get the path components for both source and destination paths. |
| std::vector<std::string> src_components; |
| mock_file.DirName().GetComponents(&src_components); |
| std::vector<std::string> dest_components; |
| proxy_file.DirName().GetComponents(&dest_components); |
| |
| // Find the common root. |
| auto mismatch_pair = |
| std::mismatch(src_components.begin(), src_components.end(), |
| dest_components.begin(), dest_components.end()); |
| |
| // For each remaining components in the |src_components|, generate the |
| // parent directory references (".."). |
| size_t src_count = |
| std::distance(mismatch_pair.first, src_components.end()); |
| std::vector<std::string> components{src_count, |
| base::FilePath::kParentDirectory}; |
| // Append the remaining components from |dest_components|. |
| components.insert(components.end(), mismatch_pair.second, |
| dest_components.end()); |
| // Finally, add the base name of the target file name. |
| components.push_back(proxy_file.BaseName().value()); |
| // Now reconstruct the relative path. |
| relative_path = base::FilePath{base::FilePath::kCurrentDirectory}; |
| for (const auto& component : components) |
| relative_path = relative_path.Append(component); |
| } |
| text.AddLine( |
| StringPrintf("#include \"%s\"", relative_path.value().c_str())); |
| text.AddBlankLine(); |
| } |
| |
| for (const auto& interface : interfaces) { |
| // If we have no proxy file, we need the abstract interfaces generated here. |
| if (proxy_file.empty()) |
| GenerateInterfaceProxyInterface(config, interface, &text); |
| GenerateInterfaceMock(config, interface, &text); |
| } |
| |
| text.AddLine(StringPrintf("#endif // %s", header_guard.c_str())); |
| return WriteTextToFile(mock_file, text); |
| } |
| |
| void ProxyGenerator::GenerateInterfaceProxyInterface( |
| const ServiceConfig& config, |
| const Interface& interface, |
| IndentedText* text) { |
| NameParser parser{interface.name}; |
| string proxy_name = parser.MakeProxyName(false); |
| string base_interface_name = proxy_name + "Interface"; |
| |
| parser.AddOpenNamespaces(text, false); |
| text->AddBlankLine(); |
| |
| text->AddLine(StringPrintf("// Abstract interface proxy for %s.", |
| parser.MakeFullCppName().c_str())); |
| text->AddComments(interface.doc_string); |
| text->AddLine(StringPrintf("class %s {", base_interface_name.c_str())); |
| text->AddLineWithOffset("public:", kScopeOffset); |
| text->PushOffset(kBlockOffset); |
| text->AddLine( |
| StringPrintf("virtual ~%s() = default;", base_interface_name.c_str())); |
| |
| for (const auto& method : interface.methods) { |
| AddMethodProxy(method, interface.name, true, text); |
| AddAsyncMethodProxy(method, interface.name, true, text); |
| } |
| for (const auto& signal : interface.signals) { |
| AddSignalHandlerRegistration(signal, interface.name, true, text); |
| } |
| AddProperties(interface, true, text); |
| text->AddBlankLine(); |
| text->AddLine("virtual const dbus::ObjectPath& GetObjectPath() const = 0;"); |
| text->AddLine("virtual dbus::ObjectProxy* GetObjectProxy() const = 0;"); |
| if (!interface.properties.empty()) { |
| if (config.object_manager.name.empty()) |
| AddInitializeProperties(proxy_name, true, text); |
| else |
| AddSetPropertyChanged(proxy_name, true, text); |
| } |
| |
| text->PopOffset(); |
| text->AddLine("};"); |
| text->AddBlankLine(); |
| |
| parser.AddCloseNamespaces(text, false); |
| text->AddBlankLine(); |
| } |
| |
| void ProxyGenerator::GenerateInterfaceProxy(const ServiceConfig& config, |
| const Interface& interface, |
| IndentedText* text) { |
| NameParser parser{interface.name}; |
| string proxy_name = parser.MakeProxyName(false); |
| string base_interface_name = proxy_name + "Interface"; |
| |
| parser.AddOpenNamespaces(text, false); |
| text->AddBlankLine(); |
| |
| text->AddLine(StringPrintf("// Interface proxy for %s.", |
| parser.MakeFullCppName().c_str())); |
| text->AddComments(interface.doc_string); |
| text->AddLine(StringPrintf("class %s final : public %s {", proxy_name.c_str(), |
| base_interface_name.c_str())); |
| text->AddLineWithOffset("public:", kScopeOffset); |
| text->PushOffset(kBlockOffset); |
| AddPropertySet(config, interface, text); |
| AddConstructor(config, interface, proxy_name, text); |
| AddDisallowCopyAndAssign(proxy_name, text); |
| text->AddBlankLine(); |
| AddDestructor(proxy_name, text); |
| for (const auto& signal : interface.signals) { |
| AddSignalHandlerRegistration(signal, interface.name, false, text); |
| } |
| AddReleaseObjectProxy(text); |
| AddGetObjectPath(text); |
| AddGetObjectProxy(text); |
| if (!interface.properties.empty()) { |
| if (config.object_manager.name.empty()) |
| AddInitializeProperties(proxy_name, false, text); |
| else |
| AddSetPropertyChanged(proxy_name, false, text); |
| AddGetProperties(text); |
| } |
| for (const auto& method : interface.methods) { |
| AddMethodProxy(method, interface.name, false, text); |
| AddAsyncMethodProxy(method, interface.name, false, text); |
| } |
| AddProperties(interface, false, text); |
| |
| text->PopOffset(); |
| text->AddBlankLine(); |
| text->AddLineWithOffset("private:", kScopeOffset); |
| |
| text->PushOffset(kBlockOffset); |
| if (!config.object_manager.name.empty() && !interface.properties.empty()) |
| AddOnPropertyChanged(text); |
| text->AddLine("scoped_refptr<dbus::Bus> bus_;"); |
| if (config.service_name.empty()) { |
| text->AddLine("std::string service_name_;"); |
| } else { |
| text->AddLine(StringPrintf("const std::string service_name_{\"%s\"};", |
| config.service_name.c_str())); |
| } |
| if (interface.path.empty()) { |
| text->AddLine("dbus::ObjectPath object_path_;"); |
| } else { |
| text->AddLine(StringPrintf("const dbus::ObjectPath object_path_{\"%s\"};", |
| interface.path.c_str())); |
| } |
| if (!config.object_manager.name.empty() && !interface.properties.empty()) { |
| text->AddLine("PropertySet* property_set_;"); |
| text->AddLine(StringPrintf( |
| "base::RepeatingCallback<void(%sInterface*, const std::string&)> " |
| "on_property_changed_;", |
| proxy_name.c_str())); |
| } |
| text->AddLine("dbus::ObjectProxy* dbus_object_proxy_;"); |
| if (config.object_manager.name.empty() && !interface.properties.empty()) |
| text->AddLine("std::unique_ptr<PropertySet> property_set_;"); |
| text->AddBlankLine(); |
| |
| if (!config.object_manager.name.empty() && !interface.properties.empty()) { |
| text->AddLine(StringPrintf( |
| "friend class %s;", |
| NameParser{config.object_manager.name}.MakeProxyName(true).c_str())); |
| } |
| text->PopOffset(); |
| text->AddLine("};"); |
| |
| text->AddBlankLine(); |
| |
| parser.AddCloseNamespaces(text, false); |
| |
| text->AddBlankLine(); |
| } |
| |
| void ProxyGenerator::GenerateInterfaceMock(const ServiceConfig& config, |
| const Interface& interface, |
| IndentedText* text) { |
| NameParser parser{interface.name}; |
| string proxy_name = parser.MakeProxyName(false); |
| string base_interface_name = proxy_name + "Interface"; |
| string mock_name = proxy_name + "Mock"; |
| |
| parser.AddOpenNamespaces(text, false); |
| text->AddBlankLine(); |
| |
| text->AddLine( |
| StringPrintf("// Mock object for %s.", base_interface_name.c_str())); |
| text->AddLine(StringPrintf("class %s : public %s {", mock_name.c_str(), |
| base_interface_name.c_str())); |
| text->AddLineWithOffset("public:", kScopeOffset); |
| text->PushOffset(kBlockOffset); |
| text->AddLine(StringPrintf("%s() = default;", mock_name.c_str())); |
| AddDisallowCopyAndAssign(mock_name, text); |
| text->AddBlankLine(); |
| |
| for (const auto& method : interface.methods) { |
| AddMethodMock(method, interface.name, text); |
| AddAsyncMethodMock(method, interface.name, text); |
| } |
| for (const auto& signal : interface.signals) { |
| AddSignalHandlerRegistrationMock(signal, text); |
| } |
| |
| DBusSignature signature; |
| for (const auto& prop : interface.properties) { |
| auto parsed_type = signature.Parse(prop.type); |
| CHECK(parsed_type && parsed_type->IsValidPropertyType()); |
| string type = parsed_type->GetInArgType(DBusType::Receiver::kProxy); |
| string name = NameParser{prop.name}.MakeVariableName(); |
| text->AddLine(StringPrintf("MOCK_CONST_METHOD0(%s, %s());", name.c_str(), |
| type.c_str())); |
| if (prop.access == "readwrite") { |
| text->AddLine( |
| StringPrintf("MOCK_METHOD2(set_%s, void(%s, " |
| "base::OnceCallback<bool>));", |
| name.c_str(), type.c_str())); |
| } |
| } |
| text->AddLine( |
| "MOCK_CONST_METHOD0(GetObjectPath, const dbus::ObjectPath&());"); |
| text->AddLine("MOCK_CONST_METHOD0(GetObjectProxy, dbus::ObjectProxy*());"); |
| if (!interface.properties.empty()) { |
| if (config.object_manager.name.empty()) { |
| text->AddLineAndPushOffsetTo("MOCK_METHOD1(InitializeProperties,", 1, |
| '('); |
| text->AddLine( |
| StringPrintf("void(base::OnceCallback<void(%sInterface*, " |
| "const std::string&)>));", |
| proxy_name.c_str())); |
| text->PopOffset(); |
| } else { |
| text->AddLineAndPushOffsetTo("MOCK_METHOD1(SetPropertyChangedCallback,", |
| 1, '('); |
| text->AddLine( |
| StringPrintf("void(const base::RepeatingCallback<void(%sInterface*, " |
| "const std::string&)>&));", |
| proxy_name.c_str())); |
| text->PopOffset(); |
| } |
| } |
| |
| text->PopOffset(); |
| text->AddLine("};"); |
| |
| parser.AddCloseNamespaces(text, false); |
| text->AddBlankLine(); |
| } |
| |
| void ProxyGenerator::AddConstructor(const ServiceConfig& config, |
| const Interface& interface, |
| const string& class_name, |
| IndentedText* text) { |
| IndentedText block; |
| vector<ParamDef> args{{"scoped_refptr<dbus::Bus>", "bus", true}}; |
| if (config.service_name.empty()) |
| args.emplace_back("std::string", "service_name", true); |
| if (interface.path.empty()) |
| args.emplace_back("dbus::ObjectPath", "object_path", true); |
| if (!config.object_manager.name.empty() && !interface.properties.empty()) |
| args.emplace_back("PropertySet", "property_set", false); |
| |
| if (args.size() == 1) { |
| block.AddLine(StringPrintf("%s(%s) :", class_name.c_str(), |
| GetParamString(args.front()).c_str())); |
| } else { |
| block.AddLine(StringPrintf("%s(", class_name.c_str())); |
| block.PushOffset(kLineContinuationOffset); |
| for (size_t i = 0; i < args.size() - 1; i++) { |
| block.AddLine(StringPrintf("%s,", GetParamString(args[i]).c_str())); |
| } |
| block.AddLine(StringPrintf("%s) :", GetParamString(args.back()).c_str())); |
| } |
| block.PushOffset(kLineContinuationOffset); |
| for (const auto& arg : args) { |
| block.AddLine(StringPrintf("%s_{%s},", arg.name.c_str(), arg.name.c_str())); |
| } |
| block.AddLine("dbus_object_proxy_{"); |
| block.AddLineWithOffset( |
| "bus_->GetObjectProxy(service_name_, object_path_)} {", |
| kLineContinuationOffset); |
| block.PopOffset(); |
| if (args.size() > 1) |
| block.PopOffset(); |
| block.AddLine("}"); |
| block.AddBlankLine(); |
| text->AddBlock(block); |
| } |
| |
| void ProxyGenerator::AddDestructor(const string& class_name, |
| IndentedText* text) { |
| IndentedText block; |
| block.AddLine(StringPrintf("~%s() override {", class_name.c_str())); |
| block.AddLine("}"); |
| text->AddBlock(block); |
| } |
| |
| void ProxyGenerator::AddReleaseObjectProxy(IndentedText* text) { |
| text->AddBlankLine(); |
| text->AddLine("void ReleaseObjectProxy(base::OnceClosure callback) {"); |
| text->AddLineWithOffset( |
| "bus_->RemoveObjectProxy(service_name_, object_path_, " |
| "std::move(callback));", |
| kBlockOffset); |
| text->AddLine("}"); |
| } |
| |
| void ProxyGenerator::AddGetObjectPath(IndentedText* text) { |
| text->AddBlankLine(); |
| text->AddLine("const dbus::ObjectPath& GetObjectPath() const override {"); |
| text->AddLineWithOffset("return object_path_;", kBlockOffset); |
| text->AddLine("}"); |
| } |
| |
| void ProxyGenerator::AddGetObjectProxy(IndentedText* text) { |
| text->AddBlankLine(); |
| text->AddLine("dbus::ObjectProxy* GetObjectProxy() const override {"); |
| text->AddLineWithOffset("return dbus_object_proxy_;", kBlockOffset); |
| text->AddLine("}"); |
| } |
| |
| void ProxyGenerator::AddInitializeProperties(const string& class_name, |
| bool declaration_only, |
| IndentedText* text) { |
| text->AddBlankLine(); |
| text->AddLine(StringPrintf("%svoid InitializeProperties(", |
| declaration_only ? "virtual " : "")); |
| text->AddLineWithOffset( |
| StringPrintf("const base::RepeatingCallback<void(%sInterface*, " |
| "const std::string&)>& callback) %s", |
| class_name.c_str(), |
| declaration_only ? "= 0;" : "override {"), |
| kLineContinuationOffset); |
| if (!declaration_only) { |
| IndentedText block; |
| block.PushOffset(kBlockOffset); |
| block.AddLine("property_set_.reset("); |
| block.AddLineWithOffset( |
| "new PropertySet(dbus_object_proxy_, base::BindRepeating(callback, " |
| "this)));", |
| kLineContinuationOffset); |
| block.AddLine("property_set_->ConnectSignals();"); |
| block.AddLine("property_set_->GetAll();"); |
| text->AddBlock(block); |
| text->AddLine("}"); |
| } |
| } |
| |
| void ProxyGenerator::AddSetPropertyChanged(const string& class_name, |
| bool declaration_only, |
| IndentedText* text) { |
| text->AddBlankLine(); |
| text->AddLine(StringPrintf("%svoid SetPropertyChangedCallback(", |
| declaration_only ? "virtual " : "")); |
| text->AddLineWithOffset( |
| StringPrintf("const base::RepeatingCallback<void(%sInterface*, " |
| "const std::string&)>& callback) %s", |
| class_name.c_str(), |
| declaration_only ? "= 0;" : "override {"), |
| kLineContinuationOffset); |
| if (!declaration_only) { |
| text->AddLineWithOffset("on_property_changed_ = callback;", kBlockOffset); |
| text->AddLine("}"); |
| } |
| } |
| |
| void ProxyGenerator::AddGetProperties(IndentedText* text) { |
| text->AddBlankLine(); |
| // Dereference and take the address since sometimes property_set_ is a raw |
| // pointer and sometimes it is a std::unique_ptr. |
| text->AddLine( |
| "const PropertySet* GetProperties() const { " |
| "return &(*property_set_); }"); |
| text->AddLine("PropertySet* GetProperties() { return &(*property_set_); }"); |
| } |
| |
| void ProxyGenerator::AddOnPropertyChanged(IndentedText* text) { |
| text->AddLine("void OnPropertyChanged(const std::string& property_name) {"); |
| text->PushOffset(kBlockOffset); |
| text->AddLine("if (!on_property_changed_.is_null())"); |
| text->PushOffset(kBlockOffset); |
| text->AddLine("on_property_changed_.Run(this, property_name);"); |
| text->PopOffset(); |
| text->PopOffset(); |
| text->AddLine("}"); |
| text->AddBlankLine(); |
| } |
| |
| void ProxyGenerator::AddSignalHandlerRegistration( |
| const Interface::Signal& signal, |
| const string& interface_name, |
| bool declaration_only, |
| IndentedText* text) { |
| IndentedText block; |
| block.AddBlankLine(); |
| block.AddLine(StringPrintf("%svoid Register%sSignalHandler(", |
| declaration_only ? "virtual " : "", |
| signal.name.c_str())); |
| block.PushOffset(kLineContinuationOffset); |
| AddSignalCallbackArg(signal, false, &block); |
| block.AddLine(StringPrintf( |
| "dbus::ObjectProxy::OnConnectedCallback on_connected_callback)%s", |
| declaration_only ? " = 0;" : " override {")); |
| if (!declaration_only) { |
| block.PopOffset(); // Method signature arguments |
| block.PushOffset(kBlockOffset); |
| block.AddLine("brillo::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("signal_callback,"); |
| block.AddLine("std::move(on_connected_callback));"); |
| block.PopOffset(); // Function call line continuation |
| block.PopOffset(); // Method body |
| block.AddLine("}"); |
| } |
| text->AddBlock(block); |
| } |
| |
| void ProxyGenerator::AddPropertySet(const ServiceConfig& config, |
| const Interface& interface, |
| IndentedText* text) { |
| if (config.object_manager.name.empty() && interface.properties.empty()) |
| return; |
| |
| IndentedText block; |
| block.AddLine("class PropertySet : public dbus::PropertySet {"); |
| block.AddLineWithOffset("public:", kScopeOffset); |
| block.PushOffset(kBlockOffset); |
| block.AddLineAndPushOffsetTo("PropertySet(dbus::ObjectProxy* object_proxy,", |
| 1, '('); |
| block.AddLine("const PropertyChangedCallback& callback)"); |
| block.PopOffset(); |
| block.PushOffset(kLineContinuationOffset); |
| block.AddLineAndPushOffsetTo(": dbus::PropertySet{object_proxy,", 1, '{'); |
| block.AddLine(StringPrintf("\"%s\",", interface.name.c_str())); |
| block.AddLine("callback} {"); |
| block.PopOffset(); |
| block.PopOffset(); |
| block.PushOffset(kBlockOffset); |
| for (const auto& prop : interface.properties) { |
| block.AddLine( |
| StringPrintf("RegisterProperty(%sName(), &%s);", prop.name.c_str(), |
| NameParser{prop.name}.MakeVariableName().c_str())); |
| } |
| block.PopOffset(); |
| block.AddLine("}"); |
| AddDisallowCopyAndAssign("PropertySet", &block); |
| block.AddBlankLine(); |
| |
| DBusSignature signature; |
| for (const auto& prop : interface.properties) { |
| auto parsed_type = signature.Parse(prop.type); |
| CHECK(parsed_type && parsed_type->IsValidPropertyType()); |
| string type = parsed_type->GetBaseType(DBusType::Direction::kExtract); |
| block.AddLine( |
| StringPrintf("brillo::dbus_utils::Property<%s> %s;", type.c_str(), |
| NameParser{prop.name}.MakeVariableName().c_str())); |
| } |
| block.AddBlankLine(); |
| |
| block.PopOffset(); |
| block.AddLine("};"); |
| block.AddBlankLine(); |
| |
| text->AddBlock(block); |
| } |
| |
| void ProxyGenerator::AddProperties(const Interface& interface, |
| bool declaration_only, |
| IndentedText* text) { |
| if (declaration_only && !interface.properties.empty()) |
| text->AddBlankLine(); |
| |
| DBusSignature signature; |
| for (const auto& prop : interface.properties) { |
| if (declaration_only) { |
| text->AddLine( |
| StringPrintf("static const char* %sName() { return \"%s\"; }", |
| prop.name.c_str(), prop.name.c_str())); |
| } |
| auto parsed_type = signature.Parse(prop.type); |
| CHECK(parsed_type && parsed_type->IsValidPropertyType()); |
| string type = parsed_type->GetInArgType(DBusType::Receiver::kProxy); |
| string name = NameParser{prop.name}.MakeVariableName(); |
| if (!declaration_only) |
| text->AddBlankLine(); |
| text->AddLine(StringPrintf( |
| "%s%s %s() const%s", declaration_only ? "virtual " : "", type.c_str(), |
| name.c_str(), declaration_only ? " = 0;" : " override {")); |
| if (!declaration_only) { |
| text->AddLineWithOffset( |
| StringPrintf("return property_set_->%s.value();", name.c_str()), |
| kBlockOffset); |
| text->AddLine("}"); |
| } |
| if (prop.access == "readwrite") { |
| if (!declaration_only) |
| text->AddBlankLine(); |
| text->AddLineAndPushOffsetTo( |
| StringPrintf("%svoid set_%s(%s value,", |
| declaration_only ? "virtual " : "", name.c_str(), |
| type.c_str()), |
| 1, '('); |
| text->AddLine(StringPrintf("base::OnceCallback<void(bool)> callback)%s", |
| declaration_only ? " = 0;" : " override {")); |
| text->PopOffset(); |
| if (!declaration_only) { |
| text->AddLineWithOffset( |
| StringPrintf("property_set_->%s.Set(value, std::move(callback));", |
| name.c_str()), |
| kBlockOffset); |
| text->AddLine("}"); |
| } |
| } |
| } |
| } |
| |
| void ProxyGenerator::AddMethodProxy(const Interface::Method& method, |
| const string& interface_name, |
| bool declaration_only, |
| IndentedText* text) { |
| IndentedText block; |
| DBusSignature signature; |
| block.AddBlankLine(); |
| block.AddComments(method.doc_string); |
| block.AddLine(StringPrintf("%sbool %s(", declaration_only ? "virtual " : "", |
| method.name.c_str())); |
| block.PushOffset(kLineContinuationOffset); |
| vector<string> argument_names; |
| int argument_number = 0; |
| for (const auto& argument : method.input_arguments) { |
| auto type = signature.Parse(argument.type); |
| CHECK(type); |
| string argument_type = type->GetInArgType(DBusType::Receiver::kProxy); |
| string argument_name = GetArgName("in", argument.name, ++argument_number); |
| argument_names.push_back(argument_name); |
| block.AddLine( |
| StringPrintf("%s %s,", argument_type.c_str(), argument_name.c_str())); |
| } |
| vector<string> out_param_names{"response.get()", "error"}; |
| for (const auto& argument : method.output_arguments) { |
| auto type = signature.Parse(argument.type); |
| CHECK(type); |
| string argument_type = type->GetOutArgType(DBusType::Receiver::kProxy); |
| string argument_name = GetArgName("out", argument.name, ++argument_number); |
| out_param_names.push_back(argument_name); |
| block.AddLine( |
| StringPrintf("%s %s,", argument_type.c_str(), argument_name.c_str())); |
| } |
| block.AddLine("brillo::ErrorPtr* error,"); |
| block.AddLine( |
| StringPrintf("int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)%s", |
| declaration_only ? " = 0;" : " override {")); |
| block.PopOffset(); |
| if (!declaration_only) { |
| block.PushOffset(kBlockOffset); |
| |
| block.AddLine( |
| "auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout("); |
| block.PushOffset(kLineContinuationOffset); |
| block.AddLine("timeout_ms,"); |
| 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(); |
| |
| block.AddLine( |
| "return response && " |
| "brillo::dbus_utils::ExtractMethodCallResults("); |
| block.PushOffset(kLineContinuationOffset); |
| block.AddLine(base::JoinString(out_param_names, ", ") + ");"); |
| block.PopOffset(); |
| block.PopOffset(); |
| block.AddLine("}"); |
| } |
| text->AddBlock(block); |
| } |
| |
| void ProxyGenerator::AddAsyncMethodProxy(const Interface::Method& method, |
| const string& interface_name, |
| bool declaration_only, |
| IndentedText* text) { |
| IndentedText block; |
| DBusSignature signature; |
| block.AddBlankLine(); |
| block.AddComments(method.doc_string); |
| block.AddLine(StringPrintf("%svoid %sAsync(", |
| declaration_only ? "virtual " : "", |
| method.name.c_str())); |
| block.PushOffset(kLineContinuationOffset); |
| vector<string> argument_names; |
| int argument_number = 0; |
| for (const auto& argument : method.input_arguments) { |
| auto type = signature.Parse(argument.type); |
| CHECK(type); |
| string argument_type = type->GetInArgType(DBusType::Receiver::kProxy); |
| string argument_name = GetArgName("in", argument.name, ++argument_number); |
| argument_names.push_back(argument_name); |
| block.AddLine( |
| StringPrintf("%s %s,", argument_type.c_str(), argument_name.c_str())); |
| } |
| vector<string> out_params; |
| for (const auto& argument : method.output_arguments) { |
| auto type = signature.Parse(argument.type); |
| CHECK(type); |
| string argument_type = type->GetCallbackArgType(); |
| if (!argument.name.empty()) |
| base::StringAppendF(&argument_type, " /*%s*/", argument.name.c_str()); |
| out_params.push_back(argument_type); |
| } |
| block.AddLine(StringPrintf("base::OnceCallback<void(%s)> success_callback,", |
| base::JoinString(out_params, ", ").c_str())); |
| block.AddLine("base::OnceCallback<void(brillo::Error*)> error_callback,"); |
| block.AddLine( |
| StringPrintf("int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)%s", |
| declaration_only ? " = 0;" : " override {")); |
| block.PopOffset(); |
| if (!declaration_only) { |
| block.PushOffset(kBlockOffset); |
| |
| block.AddLine("brillo::dbus_utils::CallMethodWithTimeout("); |
| block.PushOffset(kLineContinuationOffset); |
| block.AddLine("timeout_ms,"); |
| block.AddLine("dbus_object_proxy_,"); |
| block.AddLine(StringPrintf("\"%s\",", interface_name.c_str())); |
| block.AddLine(StringPrintf("\"%s\",", method.name.c_str())); |
| block.AddLine("std::move(success_callback),"); |
| string last_argument = "std::move(error_callback)"; |
| 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(); |
| |
| block.PopOffset(); |
| block.AddLine("}"); |
| } |
| text->AddBlock(block); |
| } |
| |
| void ProxyGenerator::AddMethodMock(const Interface::Method& method, |
| const string& /* interface_name */, |
| IndentedText* text) { |
| DBusSignature signature; |
| vector<string> arguments; |
| for (const auto& argument : method.input_arguments) { |
| auto type = signature.Parse(argument.type); |
| CHECK(type); |
| string argument_type = type->GetInArgType(DBusType::Receiver::kProxy); |
| if (!argument.name.empty()) |
| base::StringAppendF(&argument_type, " /*in_%s*/", argument.name.c_str()); |
| arguments.push_back(argument_type); |
| } |
| for (const auto& argument : method.output_arguments) { |
| auto type = signature.Parse(argument.type); |
| CHECK(type); |
| string argument_type = type->GetOutArgType(DBusType::Receiver::kProxy); |
| if (!argument.name.empty()) |
| base::StringAppendF(&argument_type, " /*out_%s*/", argument.name.c_str()); |
| arguments.push_back(argument_type); |
| } |
| arguments.push_back("brillo::ErrorPtr* /*error*/"); |
| arguments.push_back("int /*timeout_ms*/"); |
| AddMockMethodDeclaration(method.name, "bool", arguments, text); |
| } |
| |
| void ProxyGenerator::AddAsyncMethodMock(const Interface::Method& method, |
| const string& /* interface_name */, |
| IndentedText* text) { |
| DBusSignature signature; |
| vector<string> arguments; |
| for (const auto& argument : method.input_arguments) { |
| auto type = signature.Parse(argument.type); |
| CHECK(type); |
| string argument_type = type->GetInArgType(DBusType::Receiver::kProxy); |
| if (!argument.name.empty()) |
| base::StringAppendF(&argument_type, " /*in_%s*/", argument.name.c_str()); |
| arguments.push_back(argument_type); |
| } |
| vector<string> out_params; |
| for (const auto& argument : method.output_arguments) { |
| auto type = signature.Parse(argument.type); |
| CHECK(type); |
| string argument_type = type->GetCallbackArgType(); |
| if (!argument.name.empty()) |
| base::StringAppendF(&argument_type, " /*%s*/", argument.name.c_str()); |
| out_params.push_back(argument_type); |
| } |
| arguments.push_back( |
| StringPrintf("base::OnceCallback<void(%s)> /*success_callback*/", |
| base::JoinString(out_params, ", ").c_str())); |
| arguments.push_back( |
| "base::OnceCallback<void(brillo::Error*)> /*error_callback*/"); |
| arguments.push_back("int /*timeout_ms*/"); |
| AddMockMethodDeclaration(method.name + "Async", "void", arguments, text); |
| } |
| |
| void ProxyGenerator::AddMockMethodDeclaration(const string& method_name, |
| const string& return_type, |
| const vector<string>& arguments, |
| IndentedText* text) { |
| IndentedText block; |
| // GMOCK doesn't go all the way up to 11, so we need to handle methods with |
| // 11 arguments or more in a different way. |
| if (arguments.size() >= 11) { |
| block.AddLineAndPushOffsetTo( |
| StringPrintf("%s %s(%s,", return_type.c_str(), method_name.c_str(), |
| arguments.front().c_str()), |
| 1, '('); |
| for (size_t i = 1; i < arguments.size() - 1; i++) |
| block.AddLine(StringPrintf("%s,", arguments[i].c_str())); |
| block.AddLine(StringPrintf("%s) override {", arguments.back().c_str())); |
| block.PopOffset(); |
| block.PushOffset(kBlockOffset); |
| block.AddLine(StringPrintf( |
| "LOG(WARNING) << \"%s(): gmock can't handle methods with %" PRIuS |
| " arguments. You can override this method in a subclass if you need" |
| " to.\";", |
| method_name.c_str(), arguments.size())); |
| if (return_type == "void") { |
| // No return added here. |
| } else if (return_type == "bool") { |
| block.AddLine("return false;"); |
| } else { |
| LOG(FATAL) << "The return type is not supported."; |
| } |
| block.PopOffset(); |
| block.AddLine("}"); |
| } else { |
| block.AddLineAndPushOffsetTo( |
| StringPrintf("MOCK_METHOD%zu(%s,", arguments.size(), |
| method_name.c_str()), |
| 1, '('); |
| block.AddLineAndPushOffsetTo( |
| StringPrintf("%s(%s,", return_type.c_str(), arguments.front().c_str()), |
| 1, '('); |
| for (size_t i = 1; i < arguments.size() - 1; i++) |
| block.AddLine(StringPrintf("%s,", arguments[i].c_str())); |
| block.AddLine(StringPrintf("%s));", arguments.back().c_str())); |
| block.PopOffset(); |
| block.PopOffset(); |
| } |
| text->AddBlock(block); |
| } |
| |
| void ProxyGenerator::AddSignalHandlerRegistrationMock( |
| const Interface::Signal& signal, IndentedText* text) { |
| IndentedText callback_arg_text; |
| IndentedText callback_arg_real; |
| AddSignalCallbackArg(signal, true, &callback_arg_text); |
| AddSignalCallbackArg(signal, false, &callback_arg_real); |
| vector<string> arg_lines = callback_arg_text.GetLines(); |
| |
| IndentedText wrapperBlock; |
| wrapperBlock.AddLine( |
| StringPrintf("void Register%sSignalHandler(", signal.name.c_str())); |
| wrapperBlock.PushOffset(2); |
| wrapperBlock.AddBlock(callback_arg_real); |
| wrapperBlock.AddLine( |
| "dbus::ObjectProxy::OnConnectedCallback on_connected_callback) {"); |
| wrapperBlock.PopOffset(); |
| wrapperBlock.PushOffset(2); |
| wrapperBlock.AddLine(StringPrintf( |
| "DoRegister%sSignalHandler(signal_callback, &on_connected_callback);", |
| signal.name.c_str())); |
| wrapperBlock.PopOffset(); |
| wrapperBlock.AddLine("}"); |
| |
| IndentedText mockBlock; |
| mockBlock.AddLineAndPushOffsetTo( |
| StringPrintf("MOCK_METHOD2(DoRegister%sSignalHandler,", |
| signal.name.c_str()), |
| 1, '('); |
| for (size_t i = 0; i < arg_lines.size(); ++i) { |
| if (i == 0) |
| mockBlock.AddLineAndPushOffsetTo("void(" + arg_lines[i], 1, '('); |
| else |
| mockBlock.AddLine(arg_lines[i]); |
| } |
| mockBlock.AddLine( |
| "dbus::ObjectProxy::OnConnectedCallback* /*on_connected_callback*/));"); |
| |
| text->AddBlock(wrapperBlock); |
| text->AddBlock(mockBlock); |
| } |
| |
| void ProxyGenerator::AddSignalCallbackArg(const Interface::Signal& signal, |
| bool comment_arg_name, |
| IndentedText* block) { |
| DBusSignature signature; |
| string signal_callback = |
| StringPrintf("%ssignal_callback%s", comment_arg_name ? "/*" : "", |
| comment_arg_name ? "*/" : ""); |
| if (signal.arguments.empty()) { |
| block->AddLine( |
| StringPrintf("base::RepeatingClosure %s,", signal_callback.c_str())); |
| } else { |
| string last_argument; |
| string prefix{"const base::RepeatingCallback<void("}; |
| for (const auto& argument : signal.arguments) { |
| if (!last_argument.empty()) { |
| if (!prefix.empty()) { |
| block->AddLineAndPushOffsetTo( |
| StringPrintf("%s%s,", prefix.c_str(), last_argument.c_str()), 1, |
| '('); |
| prefix.clear(); |
| } else { |
| block->AddLine(StringPrintf("%s,", last_argument.c_str())); |
| } |
| } |
| auto type = signature.Parse(argument.type); |
| CHECK(type); |
| last_argument = type->GetCallbackArgType(); |
| } |
| block->AddLine(StringPrintf("%s%s)>& %s,", prefix.c_str(), |
| last_argument.c_str(), |
| signal_callback.c_str())); |
| if (prefix.empty()) { |
| block->PopOffset(); |
| } |
| } |
| } |
| |
| // static |
| void ProxyGenerator::ObjectManager::GenerateProxy( |
| const ServiceConfig& config, |
| const std::vector<Interface>& interfaces, |
| IndentedText* text) { |
| if (config.object_manager.name.empty()) |
| return; |
| |
| NameParser object_manager{config.object_manager.name}; |
| object_manager.AddOpenNamespaces(text, false); |
| text->AddBlankLine(); |
| |
| string class_name = object_manager.type_name() + "Proxy"; |
| text->AddLine( |
| StringPrintf("class %s : " |
| "public dbus::ObjectManager::Interface {", |
| class_name.c_str())); |
| text->AddLineWithOffset("public:", kScopeOffset); |
| text->PushOffset(kBlockOffset); |
| |
| AddConstructor(config, class_name, interfaces, text); |
| AddDisallowCopyAndAssign(class_name, text); |
| text->AddBlankLine(); |
| AddDestructor(class_name, interfaces, text); |
| AddGetObjectManagerProxy(text); |
| for (const auto& itf : interfaces) { |
| AddInterfaceAccessors(itf, text); |
| } |
| text->PopOffset(); |
| |
| text->AddLineWithOffset("private:", kScopeOffset); |
| text->PushOffset(kBlockOffset); |
| AddOnPropertyChanged(interfaces, text); |
| AddObjectAdded(config, interfaces, text); |
| AddObjectRemoved(interfaces, text); |
| AddCreateProperties(interfaces, class_name, text); |
| AddDataMembers(config, interfaces, class_name, text); |
| |
| text->PopOffset(); |
| text->AddLine("};"); |
| text->AddBlankLine(); |
| object_manager.AddCloseNamespaces(text, false); |
| text->AddBlankLine(); |
| } |
| |
| // static |
| void ProxyGenerator::ObjectManager::AddConstructor( |
| const ServiceConfig& config, |
| const std::string& class_name, |
| const std::vector<Interface>& interfaces, |
| IndentedText* text) { |
| if (config.service_name.empty()) { |
| text->AddLineAndPushOffsetTo( |
| StringPrintf("%s(const scoped_refptr<dbus::Bus>& bus,", |
| class_name.c_str()), |
| 1, '('); |
| text->AddLine("const std::string& service_name)"); |
| text->PopOffset(); |
| } else { |
| text->AddLine(StringPrintf("%s(const scoped_refptr<dbus::Bus>& bus)", |
| class_name.c_str())); |
| } |
| text->PushOffset(kLineContinuationOffset); |
| text->AddLine(": bus_{bus},"); |
| text->PushOffset(kBlockOffset); |
| if (config.service_name.empty()) { |
| text->AddLine("service_name_{service_name},"); |
| } |
| text->AddLine("dbus_object_manager_{bus->GetObjectManager("); |
| text->PushOffset(kLineContinuationOffset); |
| if (config.service_name.empty()) { |
| text->AddLine("service_name,"); |
| } else { |
| text->AddLine(StringPrintf("\"%s\",", config.service_name.c_str())); |
| } |
| text->AddLine(StringPrintf("dbus::ObjectPath{\"%s\"})} {", |
| config.object_manager.object_path.c_str())); |
| text->PopOffset(); |
| text->PopOffset(); |
| text->PopOffset(); |
| text->PushOffset(kBlockOffset); |
| for (const auto& itf : interfaces) { |
| text->AddLine( |
| StringPrintf("dbus_object_manager_->RegisterInterface(\"%s\", this);", |
| itf.name.c_str())); |
| } |
| text->PopOffset(); |
| text->AddLine("}"); |
| text->AddBlankLine(); |
| } |
| |
| // static |
| void ProxyGenerator::ObjectManager::AddDestructor( |
| const std::string& class_name, |
| const std::vector<Interface>& interfaces, |
| IndentedText* text) { |
| text->AddLine(StringPrintf("~%s() override {", class_name.c_str())); |
| text->PushOffset(kBlockOffset); |
| for (const auto& itf : interfaces) { |
| text->AddLine( |
| StringPrintf("dbus_object_manager_->UnregisterInterface(\"%s\");", |
| itf.name.c_str())); |
| } |
| text->PopOffset(); |
| text->AddLine("}"); |
| text->AddBlankLine(); |
| } |
| |
| // static |
| void ProxyGenerator::ObjectManager::AddGetObjectManagerProxy( |
| IndentedText* text) { |
| text->AddLine("dbus::ObjectManager* GetObjectManagerProxy() const {"); |
| text->AddLineWithOffset("return dbus_object_manager_;", kBlockOffset); |
| text->AddLine("}"); |
| text->AddBlankLine(); |
| } |
| |
| // static |
| void ProxyGenerator::ObjectManager::AddInterfaceAccessors( |
| const Interface& interface, IndentedText* text) { |
| NameParser itf_name{interface.name}; |
| string map_name = itf_name.MakeVariableName() + "_instances_"; |
| |
| // GetProxy(). |
| if (interface.path.empty()) { |
| // We have no fixed path, so there could be multiple instances of this itf. |
| text->AddLine(StringPrintf("%sInterface* Get%s(", |
| itf_name.MakeProxyName(true).c_str(), |
| itf_name.MakeProxyName(false).c_str())); |
| text->PushOffset(kLineContinuationOffset); |
| text->AddLine("const dbus::ObjectPath& object_path) {"); |
| text->PopOffset(); |
| text->PushOffset(kBlockOffset); |
| text->AddLine( |
| StringPrintf("auto p = %s.find(object_path);", map_name.c_str())); |
| text->AddLine(StringPrintf("if (p != %s.end())", map_name.c_str())); |
| text->PushOffset(kBlockOffset); |
| text->AddLine("return p->second.get();"); |
| text->PopOffset(); |
| text->AddLine("return nullptr;"); |
| text->PopOffset(); |
| text->AddLine("}"); |
| } else { |
| // We have a fixed path, so the object could be considered a "singleton". |
| // Skip the object_path parameter and return the first available instance. |
| text->AddLine(StringPrintf("%sInterface* Get%s() {", |
| itf_name.MakeProxyName(true).c_str(), |
| itf_name.MakeProxyName(false).c_str())); |
| text->PushOffset(kBlockOffset); |
| text->AddLine(StringPrintf("if (%s.empty())", map_name.c_str())); |
| text->AddLineWithOffset("return nullptr;", kBlockOffset); |
| text->AddLine( |
| StringPrintf("return %s.begin()->second.get();", map_name.c_str())); |
| text->PopOffset(); |
| text->AddLine("}"); |
| } |
| |
| // GetInstances(). |
| text->AddLine(StringPrintf( |
| "std::vector<%sInterface*> Get%sInstances() const {", |
| itf_name.MakeProxyName(true).c_str(), itf_name.type_name().c_str())); |
| text->PushOffset(kBlockOffset); |
| text->AddLine(StringPrintf("std::vector<%sInterface*> values;", |
| itf_name.MakeProxyName(true).c_str())); |
| text->AddLine(StringPrintf("values.reserve(%s.size());", map_name.c_str())); |
| text->AddLine(StringPrintf("for (const auto& pair : %s)", map_name.c_str())); |
| text->AddLineWithOffset("values.push_back(pair.second.get());", kBlockOffset); |
| text->AddLine("return values;"); |
| text->PopOffset(); |
| text->AddLine("}"); |
| |
| // SetAddedCallback(). |
| text->AddLine( |
| StringPrintf("void Set%sAddedCallback(", itf_name.type_name().c_str())); |
| text->PushOffset(kLineContinuationOffset); |
| text->AddLine(StringPrintf( |
| "const base::RepeatingCallback<void(%sInterface*)>& callback) {", |
| itf_name.MakeProxyName(true).c_str())); |
| text->PopOffset(); |
| text->PushOffset(kBlockOffset); |
| text->AddLine(StringPrintf("on_%s_added_ = callback;", |
| itf_name.MakeVariableName().c_str())); |
| text->PopOffset(); |
| text->AddLine("}"); |
| |
| // SetRemovedCallback(). |
| text->AddLine( |
| StringPrintf("void Set%sRemovedCallback(", itf_name.type_name().c_str())); |
| text->PushOffset(kLineContinuationOffset); |
| text->AddLine( |
| "const base::RepeatingCallback<void(const dbus::ObjectPath&)>& " |
| "callback) {"); |
| text->PopOffset(); |
| text->PushOffset(kBlockOffset); |
| text->AddLine(StringPrintf("on_%s_removed_ = callback;", |
| itf_name.MakeVariableName().c_str())); |
| text->PopOffset(); |
| text->AddLine("}"); |
| |
| text->AddBlankLine(); |
| } |
| |
| // static |
| void ProxyGenerator::ObjectManager::AddOnPropertyChanged( |
| const std::vector<Interface>& interfaces, IndentedText* text) { |
| // If there are no interfaces with properties, comment out parameter |
| // names for OnPropertyChanged() to prevent compiler warnings on unused |
| // function parameters. |
| auto has_props = [](const Interface& itf) { return !itf.properties.empty(); }; |
| auto itf_with_props = |
| std::find_if(interfaces.begin(), interfaces.end(), has_props); |
| if (itf_with_props == interfaces.end()) { |
| text->AddLineAndPushOffsetTo( |
| "void OnPropertyChanged(" |
| "const dbus::ObjectPath& /* object_path */,", |
| 1, '('); |
| text->AddLine("const std::string& /* interface_name */,"); |
| text->AddLine("const std::string& /* property_name */) {}"); |
| text->PopOffset(); |
| text->AddBlankLine(); |
| return; |
| } |
| text->AddLineAndPushOffsetTo( |
| "void OnPropertyChanged(" |
| "const dbus::ObjectPath& object_path,", |
| 1, '('); |
| text->AddLine("const std::string& interface_name,"); |
| text->AddLine("const std::string& property_name) {"); |
| text->PopOffset(); |
| text->PushOffset(kBlockOffset); |
| for (const auto& itf : interfaces) { |
| if (itf.properties.empty()) |
| continue; |
| |
| NameParser itf_name{itf.name}; |
| text->AddLine( |
| StringPrintf("if (interface_name == \"%s\") {", itf.name.c_str())); |
| text->PushOffset(kBlockOffset); |
| string map_name = itf_name.MakeVariableName() + "_instances_"; |
| text->AddLine( |
| StringPrintf("auto p = %s.find(object_path);", map_name.c_str())); |
| text->AddLine(StringPrintf("if (p == %s.end())", map_name.c_str())); |
| text->PushOffset(kBlockOffset); |
| text->AddLine("return;"); |
| text->PopOffset(); |
| text->AddLine("p->second->OnPropertyChanged(property_name);"); |
| text->AddLine("return;"); |
| text->PopOffset(); |
| text->AddLine("}"); |
| } |
| text->PopOffset(); |
| text->AddLine("}"); |
| text->AddBlankLine(); |
| } |
| |
| // static |
| void ProxyGenerator::ObjectManager::AddObjectAdded( |
| const ServiceConfig& config, |
| const std::vector<Interface>& interfaces, |
| IndentedText* text) { |
| text->AddLine("void ObjectAdded("); |
| text->PushOffset(kLineContinuationOffset); |
| text->AddLine("const dbus::ObjectPath& object_path,"); |
| text->AddLine("const std::string& interface_name) override {"); |
| text->PopOffset(); |
| text->PushOffset(kBlockOffset); |
| for (const auto& itf : interfaces) { |
| NameParser itf_name{itf.name}; |
| string var_name = itf_name.MakeVariableName(); |
| text->AddLine( |
| StringPrintf("if (interface_name == \"%s\") {", itf.name.c_str())); |
| text->PushOffset(kBlockOffset); |
| if (!itf.properties.empty()) { |
| text->AddLine("auto property_set ="); |
| text->PushOffset(kLineContinuationOffset); |
| text->AddLine(StringPrintf("static_cast<%s::PropertySet*>(", |
| itf_name.MakeProxyName(true).c_str())); |
| text->PushOffset(kLineContinuationOffset); |
| text->AddLine( |
| "dbus_object_manager_->GetProperties(object_path, " |
| "interface_name));"); |
| text->PopOffset(); |
| text->PopOffset(); |
| } |
| text->AddLine(StringPrintf("std::unique_ptr<%s> %s_proxy{", |
| itf_name.MakeProxyName(true).c_str(), |
| var_name.c_str())); |
| text->PushOffset(kBlockOffset); |
| string new_instance = |
| StringPrintf("new %s{bus_", itf_name.MakeProxyName(true).c_str()); |
| if (config.service_name.empty()) { |
| new_instance += ", service_name_"; |
| } |
| if (itf.path.empty()) |
| new_instance += ", object_path"; |
| if (!itf.properties.empty()) |
| new_instance += ", property_set"; |
| new_instance += "}"; |
| text->AddLine(new_instance); |
| text->PopOffset(); |
| text->AddLine("};"); |
| text->AddLine( |
| StringPrintf("auto p = %s_instances_.emplace(object_path, " |
| "std::move(%s_proxy));", |
| var_name.c_str(), var_name.c_str())); |
| text->AddLine( |
| StringPrintf("if (!on_%s_added_.is_null())", var_name.c_str())); |
| text->PushOffset(kBlockOffset); |
| text->AddLine(StringPrintf("on_%s_added_.Run(p.first->second.get());", |
| var_name.c_str())); |
| text->PopOffset(); |
| text->AddLine("return;"); |
| text->PopOffset(); |
| text->AddLine("}"); |
| } |
| text->PopOffset(); |
| text->AddLine("}"); |
| text->AddBlankLine(); |
| } |
| |
| // static |
| void ProxyGenerator::ObjectManager::AddObjectRemoved( |
| const std::vector<Interface>& interfaces, IndentedText* text) { |
| text->AddLine("void ObjectRemoved("); |
| text->PushOffset(kLineContinuationOffset); |
| text->AddLine("const dbus::ObjectPath& object_path,"); |
| text->AddLine("const std::string& interface_name) override {"); |
| text->PopOffset(); |
| text->PushOffset(kBlockOffset); |
| for (const auto& itf : interfaces) { |
| NameParser itf_name{itf.name}; |
| string var_name = itf_name.MakeVariableName(); |
| text->AddLine( |
| StringPrintf("if (interface_name == \"%s\") {", itf.name.c_str())); |
| text->PushOffset(kBlockOffset); |
| text->AddLine(StringPrintf("auto p = %s_instances_.find(object_path);", |
| var_name.c_str())); |
| text->AddLine( |
| StringPrintf("if (p != %s_instances_.end()) {", var_name.c_str())); |
| text->PushOffset(kBlockOffset); |
| text->AddLine( |
| StringPrintf("if (!on_%s_removed_.is_null())", var_name.c_str())); |
| text->PushOffset(kBlockOffset); |
| text->AddLine( |
| StringPrintf("on_%s_removed_.Run(object_path);", var_name.c_str())); |
| text->PopOffset(); |
| text->AddLine(StringPrintf("%s_instances_.erase(p);", var_name.c_str())); |
| text->PopOffset(); |
| text->AddLine("}"); |
| text->AddLine("return;"); |
| text->PopOffset(); |
| text->AddLine("}"); |
| } |
| text->PopOffset(); |
| text->AddLine("}"); |
| text->AddBlankLine(); |
| } |
| |
| // static |
| void ProxyGenerator::ObjectManager::AddCreateProperties( |
| const std::vector<Interface>& interfaces, |
| const std::string& class_name, |
| IndentedText* text) { |
| text->AddLine("dbus::PropertySet* CreateProperties("); |
| text->PushOffset(kLineContinuationOffset); |
| text->AddLine("dbus::ObjectProxy* object_proxy,"); |
| text->AddLine("const dbus::ObjectPath& object_path,"); |
| text->AddLine("const std::string& interface_name) override {"); |
| text->PopOffset(); |
| text->PushOffset(kBlockOffset); |
| for (const auto& itf : interfaces) { |
| NameParser itf_name{itf.name}; |
| text->AddLine( |
| StringPrintf("if (interface_name == \"%s\") {", itf.name.c_str())); |
| text->PushOffset(kBlockOffset); |
| text->AddLine(StringPrintf("return new %s::PropertySet{", |
| itf_name.MakeProxyName(true).c_str())); |
| text->PushOffset(kLineContinuationOffset); |
| text->AddLine("object_proxy,"); |
| text->AddLineAndPushOffsetTo( |
| StringPrintf("base::BindRepeating(&%s::OnPropertyChanged,", |
| class_name.c_str()), |
| 1, '('); |
| text->AddLine("weak_ptr_factory_.GetWeakPtr(),"); |
| text->AddLine("object_path,"); |
| text->AddLine("interface_name)"); |
| text->PopOffset(); |
| text->PopOffset(); |
| text->AddLine("};"); |
| text->PopOffset(); |
| text->AddLine("}"); |
| } |
| text->AddLineAndPushOffsetTo( |
| "LOG(FATAL) << \"Creating properties for " |
| "unsupported interface \"", |
| 1, ' '); |
| text->AddLine("<< interface_name;"); |
| text->PopOffset(); |
| text->AddLine("return nullptr;"); |
| text->PopOffset(); |
| text->AddLine("}"); |
| text->AddBlankLine(); |
| } |
| |
| // static |
| void ProxyGenerator::ObjectManager::AddDataMembers( |
| const ServiceConfig& config, |
| const std::vector<Interface>& interfaces, |
| const std::string& class_name, |
| IndentedText* text) { |
| text->AddLine("scoped_refptr<dbus::Bus> bus_;"); |
| if (config.service_name.empty()) { |
| text->AddLine("std::string service_name_;"); |
| } |
| text->AddLine("dbus::ObjectManager* dbus_object_manager_;"); |
| for (const auto& itf : interfaces) { |
| NameParser itf_name{itf.name}; |
| string var_name = itf_name.MakeVariableName(); |
| text->AddLineAndPushOffsetTo("std::map<dbus::ObjectPath,", 1, '<'); |
| text->AddLine(StringPrintf("std::unique_ptr<%s>> %s_instances_;", |
| itf_name.MakeProxyName(true).c_str(), |
| var_name.c_str())); |
| text->PopOffset(); |
| text->AddLine(StringPrintf( |
| "base::RepeatingCallback<void(%sInterface*)> on_%s_added_;", |
| itf_name.MakeProxyName(true).c_str(), var_name.c_str())); |
| text->AddLine( |
| StringPrintf("base::RepeatingCallback<void(const dbus::ObjectPath&)> " |
| "on_%s_removed_;", |
| var_name.c_str())); |
| } |
| text->AddLine(StringPrintf( |
| "base::WeakPtrFactory<%s> weak_ptr_factory_{this};", class_name.c_str())); |
| } |
| |
| string ProxyGenerator::GetHandlerNameForSignal(const string& signal) { |
| return StringPrintf("On%sSignal", signal.c_str()); |
| } |
| |
| } // namespace chromeos_dbus_bindings |