1 // Copyright 2014 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chromeos-dbus-bindings/proxy_generator.h"
6 
7 #include <utility>
8 
9 #include <base/files/file_path.h>
10 #include <base/format_macros.h>
11 #include <base/logging.h>
12 #include <base/strings/stringprintf.h>
13 #include <brillo/strings/string_utils.h>
14 
15 #include "chromeos-dbus-bindings/dbus_signature.h"
16 #include "chromeos-dbus-bindings/indented_text.h"
17 #include "chromeos-dbus-bindings/name_parser.h"
18 
19 using base::StringPrintf;
20 using std::pair;
21 using std::string;
22 using std::vector;
23 
24 namespace chromeos_dbus_bindings {
25 
26 namespace {
27 // Helper struct to encapsulate information about method call parameter during
28 // code generation.
29 struct ParamDef {
ParamDefchromeos_dbus_bindings::__anon44aa6a2b0111::ParamDef30   ParamDef(const string& param_type, const string& param_name, bool param_ref)
31       : type(param_type), name(param_name), is_const_ref(param_ref) {}
32 
33   string type;
34   string name;
35   bool is_const_ref;
36 };
37 
GetParamString(const ParamDef & param_def)38 string GetParamString(const ParamDef& param_def) {
39   return StringPrintf(param_def.is_const_ref ? "const %s& %s" : "%s* %s",
40                       param_def.type.c_str(), param_def.name.c_str());
41 }
42 }  // anonymous namespace
43 
44 // static
GenerateProxies(const ServiceConfig & config,const std::vector<Interface> & interfaces,const base::FilePath & output_file)45 bool ProxyGenerator::GenerateProxies(
46     const ServiceConfig& config,
47     const std::vector<Interface>& interfaces,
48     const base::FilePath& output_file) {
49   IndentedText text;
50 
51   text.AddLine("// Automatic generation of D-Bus interfaces:");
52   for (const auto& interface : interfaces) {
53     text.AddLine(StringPrintf("//  - %s", interface.name.c_str()));
54   }
55   string header_guard = GenerateHeaderGuard(output_file);
56   text.AddLine(StringPrintf("#ifndef %s", header_guard.c_str()));
57   text.AddLine(StringPrintf("#define %s", header_guard.c_str()));
58   text.AddLine("#include <memory>");
59   text.AddLine("#include <string>");
60   text.AddLine("#include <vector>");
61   text.AddBlankLine();
62   text.AddLine("#include <base/bind.h>");
63   text.AddLine("#include <base/callback.h>");
64   text.AddLine("#include <base/logging.h>");
65   text.AddLine("#include <base/macros.h>");
66   text.AddLine("#include <base/memory/ref_counted.h>");
67   text.AddLine("#include <brillo/any.h>");
68   text.AddLine("#include <brillo/dbus/dbus_method_invoker.h>");
69   text.AddLine("#include <brillo/dbus/dbus_property.h>");
70   text.AddLine("#include <brillo/dbus/dbus_signal_handler.h>");
71   text.AddLine("#include <brillo/errors/error.h>");
72   text.AddLine("#include <brillo/variant_dictionary.h>");
73   text.AddLine("#include <dbus/bus.h>");
74   text.AddLine("#include <dbus/message.h>");
75   text.AddLine("#include <dbus/object_manager.h>");
76   text.AddLine("#include <dbus/object_path.h>");
77   text.AddLine("#include <dbus/object_proxy.h>");
78   text.AddBlankLine();
79 
80   if (!config.object_manager.name.empty()) {
81     // Add forward-declaration for Object Manager proxy class.
82     NameParser parser{config.object_manager.name};
83     parser.AddOpenNamespaces(&text, false);
84     text.AddLine(StringPrintf("class %s;",
85                               parser.MakeProxyName(false).c_str()));
86     parser.AddCloseNamespaces(&text, false);
87     text.AddBlankLine();
88   }
89 
90   for (const auto& interface : interfaces) {
91     GenerateInterfaceProxyInterface(config, interface, &text);
92     GenerateInterfaceProxy(config, interface, &text);
93   }
94 
95   ObjectManager::GenerateProxy(config, interfaces, &text);
96 
97   text.AddLine(StringPrintf("#endif  // %s", header_guard.c_str()));
98   return WriteTextToFile(output_file, text);
99 }
100 
101 // static
GenerateMocks(const ServiceConfig & config,const std::vector<Interface> & interfaces,const base::FilePath & mock_file,const base::FilePath & proxy_file,bool use_literal_proxy_file)102 bool ProxyGenerator::GenerateMocks(const ServiceConfig& config,
103                                    const std::vector<Interface>& interfaces,
104                                    const base::FilePath& mock_file,
105                                    const base::FilePath& proxy_file,
106                                    bool use_literal_proxy_file) {
107   IndentedText text;
108 
109   text.AddLine("// Automatic generation of D-Bus interface mock proxies for:");
110   for (const auto& interface : interfaces) {
111     text.AddLine(StringPrintf("//  - %s", interface.name.c_str()));
112   }
113   string header_guard = GenerateHeaderGuard(mock_file);
114   text.AddLine(StringPrintf("#ifndef %s", header_guard.c_str()));
115   text.AddLine(StringPrintf("#define %s", header_guard.c_str()));
116   text.AddLine("#include <string>");
117   text.AddLine("#include <vector>");
118   text.AddBlankLine();
119   text.AddLine("#include <base/callback_forward.h>");
120   text.AddLine("#include <base/logging.h>");
121   text.AddLine("#include <base/macros.h>");
122   text.AddLine("#include <brillo/any.h>");
123   text.AddLine("#include <brillo/errors/error.h>");
124   text.AddLine("#include <brillo/variant_dictionary.h>");
125   text.AddLine("#include <gmock/gmock.h>");
126   text.AddBlankLine();
127 
128   if (!proxy_file.empty()) {
129     // If we have a proxy header file, it would have the proxy interfaces we
130     // need to base our mocks on, so we need to include that header file.
131     base::FilePath relative_path;
132     if (use_literal_proxy_file) {
133       relative_path = proxy_file;
134     } else {
135       // Generate a relative path from |mock_file| to |proxy_file|.
136 
137       // First, get the path components for both source and destination paths.
138       std::vector<base::FilePath::StringType> src_components;
139       mock_file.DirName().GetComponents(&src_components);
140       std::vector<base::FilePath::StringType> dest_components;
141       proxy_file.DirName().GetComponents(&dest_components);
142 
143       // Find the common root.
144 
145       // I wish we had C++14 and its 4-parameter version of std::mismatch()...
146       auto src_end = src_components.end();
147       if (src_components.size() > dest_components.size())
148         src_end = src_components.begin() + dest_components.size();
149 
150       auto mismatch_pair = std::mismatch(src_components.begin(), src_end,
151                                          dest_components.begin());
152 
153       // For each remaining components in the |src_components|, generate the
154       // parent directory references ("..").
155       size_t src_count = std::distance(mismatch_pair.first,
156                                        src_components.end());
157       std::vector<base::FilePath::StringType> components{
158           src_count, base::FilePath::kParentDirectory};
159       // Append the remaining components from |dest_components|.
160       components.insert(components.end(),
161                         mismatch_pair.second, dest_components.end());
162       // Finally, add the base name of the target file name.
163       components.push_back(proxy_file.BaseName().value());
164       // Now reconstruct the relative path.
165       relative_path = base::FilePath{base::FilePath::kCurrentDirectory};
166       for (const auto& component : components)
167         relative_path = relative_path.Append(component);
168     }
169     text.AddLine(StringPrintf("#include \"%s\"",
170                               relative_path.value().c_str()));
171     text.AddBlankLine();
172   }
173 
174   for (const auto& interface : interfaces) {
175     // If we have no proxy file, we need the abstract interfaces generated here.
176     if (proxy_file.empty())
177       GenerateInterfaceProxyInterface(config, interface, &text);
178     GenerateInterfaceMock(config, interface, &text);
179   }
180 
181   text.AddLine(StringPrintf("#endif  // %s", header_guard.c_str()));
182   return WriteTextToFile(mock_file, text);
183 }
184 
185 // static
GenerateInterfaceProxyInterface(const ServiceConfig & config,const Interface & interface,IndentedText * text)186 void ProxyGenerator::GenerateInterfaceProxyInterface(
187     const ServiceConfig& config,
188     const Interface& interface,
189     IndentedText* text) {
190   NameParser parser{interface.name};
191   string proxy_name = parser.MakeProxyName(false);
192   string base_interface_name = proxy_name + "Interface";
193 
194   parser.AddOpenNamespaces(text, false);
195   text->AddBlankLine();
196 
197   text->AddLine(StringPrintf("// Abstract interface proxy for %s.",
198                              parser.MakeFullCppName().c_str()));
199   text->AddComments(interface.doc_string);
200   text->AddLine(StringPrintf("class %s {", base_interface_name.c_str()));
201   text->AddLineWithOffset("public:", kScopeOffset);
202   text->PushOffset(kBlockOffset);
203   text->AddLine(
204       StringPrintf("virtual ~%s() = default;", base_interface_name.c_str()));
205 
206   for (const auto& method : interface.methods) {
207     AddMethodProxy(method, interface.name, true, text);
208     AddAsyncMethodProxy(method, interface.name, true, text);
209   }
210   for (const auto& signal : interface.signals) {
211     AddSignalHandlerRegistration(signal, interface.name, true, text);
212   }
213   AddProperties(config, interface, true, text);
214   text->AddBlankLine();
215   text->AddLine("virtual const dbus::ObjectPath& GetObjectPath() const = 0;");
216   if (!config.object_manager.name.empty() && !interface.properties.empty())
217     AddPropertyPublicMethods(proxy_name, true, text);
218 
219   text->PopOffset();
220   text->AddLine("};");
221   text->AddBlankLine();
222 
223   parser.AddCloseNamespaces(text, false);
224   text->AddBlankLine();
225 }
226 
227 // static
GenerateInterfaceProxy(const ServiceConfig & config,const Interface & interface,IndentedText * text)228 void ProxyGenerator::GenerateInterfaceProxy(const ServiceConfig& config,
229                                             const Interface& interface,
230                                             IndentedText* text) {
231   NameParser parser{interface.name};
232   string proxy_name = parser.MakeProxyName(false);
233   string base_interface_name = proxy_name + "Interface";
234 
235   parser.AddOpenNamespaces(text, false);
236   text->AddBlankLine();
237 
238   text->AddLine(StringPrintf("// Interface proxy for %s.",
239                              parser.MakeFullCppName().c_str()));
240   text->AddComments(interface.doc_string);
241   text->AddLine(StringPrintf("class %s final : public %s {",
242                              proxy_name.c_str(), base_interface_name.c_str()));
243   text->AddLineWithOffset("public:", kScopeOffset);
244   text->PushOffset(kBlockOffset);
245   AddPropertySet(config, interface, text);
246   AddConstructor(config, interface, proxy_name, text);
247   AddDestructor(proxy_name, text);
248   for (const auto& signal : interface.signals) {
249     AddSignalHandlerRegistration(signal, interface.name, false, text);
250   }
251   AddReleaseObjectProxy(text);
252   AddGetObjectPath(text);
253   AddGetObjectProxy(text);
254   if (!config.object_manager.name.empty() && !interface.properties.empty())
255     AddPropertyPublicMethods(proxy_name, false, text);
256   for (const auto& method : interface.methods) {
257     AddMethodProxy(method, interface.name, false, text);
258     AddAsyncMethodProxy(method, interface.name, false, text);
259   }
260   AddProperties(config, interface, false, text);
261 
262   text->PopOffset();
263   text->AddBlankLine();
264   text->AddLineWithOffset("private:", kScopeOffset);
265 
266   text->PushOffset(kBlockOffset);
267   if (!config.object_manager.name.empty() && !interface.properties.empty())
268     AddOnPropertyChanged(text);
269   text->AddLine("scoped_refptr<dbus::Bus> bus_;");
270   if (config.service_name.empty()) {
271     text->AddLine("std::string service_name_;");
272   } else {
273     text->AddLine(StringPrintf("const std::string service_name_{\"%s\"};",
274                                config.service_name.c_str()));
275   }
276   if (interface.path.empty()) {
277     text->AddLine("dbus::ObjectPath object_path_;");
278   } else {
279     text->AddLine(StringPrintf("const dbus::ObjectPath object_path_{\"%s\"};",
280                                interface.path.c_str()));
281   }
282   if (!config.object_manager.name.empty() && !interface.properties.empty()) {
283     text->AddLine("PropertySet* property_set_;");
284     text->AddLine(
285         StringPrintf("base::Callback<void(%sInterface*, const std::string&)> "
286                      "on_property_changed_;",
287                      proxy_name.c_str()));
288   }
289   text->AddLine("dbus::ObjectProxy* dbus_object_proxy_;");
290   text->AddBlankLine();
291 
292   if (!config.object_manager.name.empty() && !interface.properties.empty()) {
293     text->AddLine(StringPrintf(
294         "friend class %s;",
295         NameParser{config.object_manager.name}.MakeProxyName(true).c_str()));
296   }
297   text->AddLine(StringPrintf("DISALLOW_COPY_AND_ASSIGN(%s);",
298                              proxy_name.c_str()));
299   text->PopOffset();
300   text->AddLine("};");
301 
302   text->AddBlankLine();
303 
304   parser.AddCloseNamespaces(text, false);
305 
306   text->AddBlankLine();
307 }
308 
309 // static
GenerateInterfaceMock(const ServiceConfig & config,const Interface & interface,IndentedText * text)310 void ProxyGenerator::GenerateInterfaceMock(const ServiceConfig& config,
311                                            const Interface& interface,
312                                            IndentedText* text) {
313   NameParser parser{interface.name};
314   string proxy_name = parser.MakeProxyName(false);
315   string base_interface_name = proxy_name + "Interface";
316   string mock_name = proxy_name + "Mock";
317 
318   parser.AddOpenNamespaces(text, false);
319   text->AddBlankLine();
320 
321   text->AddLine(StringPrintf("// Mock object for %s.",
322                              base_interface_name.c_str()));
323   text->AddLine(StringPrintf("class %s : public %s {",
324                              mock_name.c_str(), base_interface_name.c_str()));
325   text->AddLineWithOffset("public:", kScopeOffset);
326   text->PushOffset(kBlockOffset);
327   text->AddLine(StringPrintf("%s() = default;", mock_name.c_str()));
328   text->AddBlankLine();
329 
330   for (const auto& method : interface.methods) {
331     AddMethodMock(method, interface.name, text);
332     AddAsyncMethodMock(method, interface.name, text);
333   }
334   for (const auto& signal : interface.signals) {
335     AddSignalHandlerRegistrationMock(signal, text);
336   }
337 
338   DbusSignature signature;
339   for (const auto& prop : interface.properties) {
340     string type;
341     CHECK(signature.Parse(prop.type, &type));
342     MakeConstReferenceIfNeeded(&type);
343     string name = NameParser{prop.name}.MakeVariableName();
344     text->AddLine(StringPrintf("MOCK_CONST_METHOD0(%s, %s());",
345                                name.c_str(), type.c_str()));
346     if (prop.access == "readwrite") {
347       text->AddLine(StringPrintf("MOCK_METHOD2(set_%s, void(%s, "
348                                  "const base::Callback<bool>&));",
349                                  name.c_str(), type.c_str()));
350     }
351   }
352   text->AddLine(
353       "MOCK_CONST_METHOD0(GetObjectPath, const dbus::ObjectPath&());");
354   if (!config.object_manager.name.empty() && !interface.properties.empty()) {
355     text->AddLineAndPushOffsetTo(
356         "MOCK_METHOD1(SetPropertyChangedCallback,", 1, '(');
357     text->AddLine(StringPrintf(
358         "void(const base::Callback<void(%sInterface*, const std::string&)>&));",
359         proxy_name.c_str()));
360     text->PopOffset();
361   }
362 
363   text->PopOffset();
364   text->AddBlankLine();
365   text->AddLineWithOffset("private:", kScopeOffset);
366   text->AddLineWithOffset(StringPrintf("DISALLOW_COPY_AND_ASSIGN(%s);",
367                                        mock_name.c_str()),
368                           kBlockOffset);
369   text->AddLine("};");
370 
371   parser.AddCloseNamespaces(text, false);
372   text->AddBlankLine();
373 }
374 
375 // static
AddConstructor(const ServiceConfig & config,const Interface & interface,const string & class_name,IndentedText * text)376 void ProxyGenerator::AddConstructor(const ServiceConfig& config,
377                                     const Interface& interface,
378                                     const string& class_name,
379                                     IndentedText* text) {
380   IndentedText block;
381   vector<ParamDef> args{{"scoped_refptr<dbus::Bus>", "bus", true}};
382   if (config.service_name.empty())
383     args.emplace_back("std::string", "service_name", true);
384   if (interface.path.empty())
385     args.emplace_back("dbus::ObjectPath", "object_path", true);
386   if (!config.object_manager.name.empty() && !interface.properties.empty())
387     args.emplace_back("PropertySet", "property_set", false);
388 
389   if (args.size() == 1) {
390     block.AddLine(StringPrintf("%s(%s) :", class_name.c_str(),
391                                GetParamString(args.front()).c_str()));
392   } else {
393     block.AddLine(StringPrintf("%s(", class_name.c_str()));
394     block.PushOffset(kLineContinuationOffset);
395     for (size_t i = 0; i < args.size() - 1; i++) {
396       block.AddLine(StringPrintf("%s,", GetParamString(args[i]).c_str()));
397     }
398     block.AddLine(StringPrintf("%s) :", GetParamString(args.back()).c_str()));
399   }
400   block.PushOffset(kLineContinuationOffset);
401   for (const auto& arg : args) {
402     block.AddLine(StringPrintf("%s_{%s},", arg.name.c_str(),
403                                arg.name.c_str()));
404   }
405   block.AddLine("dbus_object_proxy_{");
406   block.AddLineWithOffset(
407       "bus_->GetObjectProxy(service_name_, object_path_)} {",
408       kLineContinuationOffset);
409   block.PopOffset();
410   if (args.size() > 1)
411     block.PopOffset();
412   block.AddLine("}");
413   block.AddBlankLine();
414   text->AddBlock(block);
415 }
416 
417 // static
AddDestructor(const string & class_name,IndentedText * text)418 void ProxyGenerator::AddDestructor(const string& class_name,
419                                    IndentedText* text) {
420   IndentedText block;
421   block.AddLine(StringPrintf("~%s() override {", class_name.c_str()));
422   block.AddLine("}");
423   text->AddBlock(block);
424 }
425 
426 // static
AddReleaseObjectProxy(IndentedText * text)427 void ProxyGenerator::AddReleaseObjectProxy(IndentedText* text) {
428   text->AddBlankLine();
429   text->AddLine("void ReleaseObjectProxy(const base::Closure& callback) {");
430   text->AddLineWithOffset(
431       "bus_->RemoveObjectProxy(service_name_, object_path_, callback);",
432       kBlockOffset);
433   text->AddLine("}");
434 }
435 
436 // static
AddGetObjectPath(IndentedText * text)437 void ProxyGenerator::AddGetObjectPath(IndentedText* text) {
438   text->AddBlankLine();
439   text->AddLine("const dbus::ObjectPath& GetObjectPath() const override {");
440   text->AddLineWithOffset("return object_path_;", kBlockOffset);
441   text->AddLine("}");
442 }
443 
444 // static
AddGetObjectProxy(IndentedText * text)445 void ProxyGenerator::AddGetObjectProxy(IndentedText* text) {
446   text->AddBlankLine();
447   text->AddLine("dbus::ObjectProxy* GetObjectProxy() const { "
448                 "return dbus_object_proxy_; }");
449 }
450 
451 // static
AddPropertyPublicMethods(const string & class_name,bool declaration_only,IndentedText * text)452 void ProxyGenerator::AddPropertyPublicMethods(const string& class_name,
453                                               bool declaration_only,
454                                               IndentedText* text) {
455   text->AddBlankLine();
456   text->AddLine(StringPrintf("%svoid SetPropertyChangedCallback(",
457                              declaration_only ? "virtual " : ""));
458   text->AddLineWithOffset(
459       StringPrintf("const base::Callback<void(%sInterface*, "
460                    "const std::string&)>& callback) %s",
461                    class_name.c_str(),
462                    declaration_only ? "= 0;" : "override {"),
463       kLineContinuationOffset);
464   if (!declaration_only) {
465     text->AddLineWithOffset("on_property_changed_ = callback;", kBlockOffset);
466     text->AddLine("}");
467     text->AddBlankLine();
468 
469     text->AddLine(
470         "const PropertySet* GetProperties() const { return property_set_; }");
471     text->AddLine("PropertySet* GetProperties() { return property_set_; }");
472   }
473 }
474 
475 // static
AddOnPropertyChanged(IndentedText * text)476 void ProxyGenerator::AddOnPropertyChanged(IndentedText* text) {
477   text->AddLine("void OnPropertyChanged(const std::string& property_name) {");
478   text->PushOffset(kBlockOffset);
479   text->AddLine("if (!on_property_changed_.is_null())");
480   text->PushOffset(kBlockOffset);
481   text->AddLine("on_property_changed_.Run(this, property_name);");
482   text->PopOffset();
483   text->PopOffset();
484   text->AddLine("}");
485   text->AddBlankLine();
486 }
487 
AddSignalHandlerRegistration(const Interface::Signal & signal,const string & interface_name,bool declaration_only,IndentedText * text)488 void ProxyGenerator::AddSignalHandlerRegistration(
489       const Interface::Signal& signal,
490       const string& interface_name,
491       bool declaration_only,
492       IndentedText* text) {
493   IndentedText block;
494   block.AddBlankLine();
495   block.AddLine(StringPrintf("%svoid Register%sSignalHandler(",
496                              declaration_only ? "virtual " : "",
497                              signal.name.c_str()));
498   block.PushOffset(kLineContinuationOffset);
499   AddSignalCallbackArg(signal, false, &block);
500   block.AddLine(StringPrintf(
501       "dbus::ObjectProxy::OnConnectedCallback on_connected_callback)%s",
502       declaration_only ? " = 0;" : " override {"));
503   if (!declaration_only) {
504     block.PopOffset();  // Method signature arguments
505     block.PushOffset(kBlockOffset);
506     block.AddLine("brillo::dbus_utils::ConnectToSignal(");
507     block.PushOffset(kLineContinuationOffset);
508     block.AddLine("dbus_object_proxy_,");
509     block.AddLine(StringPrintf("\"%s\",", interface_name.c_str()));
510     block.AddLine(StringPrintf("\"%s\",", signal.name.c_str()));
511     block.AddLine("signal_callback,");
512     block.AddLine("on_connected_callback);");
513     block.PopOffset();  // Function call line continuation
514     block.PopOffset();  // Method body
515     block.AddLine("}");
516   }
517   text->AddBlock(block);
518 }
519 
520 // static
AddPropertySet(const ServiceConfig & config,const Interface & interface,IndentedText * text)521 void ProxyGenerator::AddPropertySet(const ServiceConfig& config,
522                                     const Interface& interface,
523                                     IndentedText* text) {
524   // Must have ObjectManager in order for property system to work correctly.
525   if (config.object_manager.name.empty())
526     return;
527 
528   IndentedText block;
529   block.AddLine("class PropertySet : public dbus::PropertySet {");
530   block.AddLineWithOffset("public:", kScopeOffset);
531   block.PushOffset(kBlockOffset);
532   block.AddLineAndPushOffsetTo("PropertySet(dbus::ObjectProxy* object_proxy,",
533                                1, '(');
534   block.AddLine("const PropertyChangedCallback& callback)");
535   block.PopOffset();
536   block.PushOffset(kLineContinuationOffset);
537   block.AddLineAndPushOffsetTo(": dbus::PropertySet{object_proxy,", 1, '{');
538   block.AddLine(StringPrintf("\"%s\",", interface.name.c_str()));
539   block.AddLine("callback} {");
540   block.PopOffset();
541   block.PopOffset();
542   block.PushOffset(kBlockOffset);
543   for (const auto& prop : interface.properties) {
544     block.AddLine(
545         StringPrintf("RegisterProperty(%sName(), &%s);",
546                      prop.name.c_str(),
547                      NameParser{prop.name}.MakeVariableName().c_str()));
548   }
549   block.PopOffset();
550   block.AddLine("}");
551   block.AddBlankLine();
552 
553   DbusSignature signature;
554   for (const auto& prop : interface.properties) {
555     string type;
556     CHECK(signature.Parse(prop.type, &type));
557     block.AddLine(
558         StringPrintf("brillo::dbus_utils::Property<%s> %s;",
559                      type.c_str(),
560                      NameParser{prop.name}.MakeVariableName().c_str()));
561   }
562   block.AddBlankLine();
563 
564   block.PopOffset();
565   block.AddLineWithOffset("private:", kScopeOffset);
566   block.AddLineWithOffset("DISALLOW_COPY_AND_ASSIGN(PropertySet);",
567                           kBlockOffset);
568   block.AddLine("};");
569   block.AddBlankLine();
570 
571   text->AddBlock(block);
572 }
573 
574 // static
AddProperties(const ServiceConfig & config,const Interface & interface,bool declaration_only,IndentedText * text)575 void ProxyGenerator::AddProperties(const ServiceConfig& config,
576                                    const Interface& interface,
577                                    bool declaration_only,
578                                    IndentedText* text) {
579   // Must have ObjectManager in order for property system to work correctly.
580   if (config.object_manager.name.empty())
581     return;
582 
583   if (declaration_only && !interface.properties.empty())
584     text->AddBlankLine();
585 
586   DbusSignature signature;
587   for (const auto& prop : interface.properties) {
588     if (declaration_only) {
589       text->AddLine(
590           StringPrintf("static const char* %sName() { return \"%s\"; }",
591                        prop.name.c_str(),
592                        prop.name.c_str()));
593     }
594     string type;
595     CHECK(signature.Parse(prop.type, &type));
596     MakeConstReferenceIfNeeded(&type);
597     string name = NameParser{prop.name}.MakeVariableName();
598     if (!declaration_only)
599       text->AddBlankLine();
600     text->AddLine(
601         StringPrintf("%s%s %s() const%s",
602                      declaration_only ? "virtual " : "",
603                      type.c_str(),
604                      name.c_str(),
605                      declaration_only ? " = 0;" : " override {"));
606     if (!declaration_only) {
607       text->AddLineWithOffset(
608           StringPrintf("return property_set_->%s.value();", name.c_str()),
609           kBlockOffset);
610       text->AddLine("}");
611     }
612     if (prop.access == "readwrite") {
613       if (!declaration_only)
614         text->AddBlankLine();
615       text->AddLineAndPushOffsetTo(
616           StringPrintf("%svoid set_%s(%s value,",
617                        declaration_only ? "virtual " : "",
618                        name.c_str(),
619                        type.c_str()),
620           1, '(');
621       text->AddLine(
622           StringPrintf("const base::Callback<void(bool)>& callback)%s",
623                        declaration_only ? " = 0;" : " override {"));
624       text->PopOffset();
625       if (!declaration_only) {
626         text->AddLineWithOffset(
627             StringPrintf("property_set_->%s.Set(value, callback);", name.c_str()),
628             kBlockOffset);
629         text->AddLine("}");
630       }
631     }
632   }
633 }
634 
635 // static
AddMethodProxy(const Interface::Method & method,const string & interface_name,bool declaration_only,IndentedText * text)636 void ProxyGenerator::AddMethodProxy(const Interface::Method& method,
637                                     const string& interface_name,
638                                     bool declaration_only,
639                                     IndentedText* text) {
640   IndentedText block;
641   DbusSignature signature;
642   block.AddBlankLine();
643   block.AddComments(method.doc_string);
644   block.AddLine(StringPrintf("%sbool %s(",
645                              declaration_only ? "virtual " : "",
646                              method.name.c_str()));
647   block.PushOffset(kLineContinuationOffset);
648   vector<string> argument_names;
649   int argument_number = 0;
650   for (const auto& argument : method.input_arguments) {
651     string argument_type;
652     CHECK(signature.Parse(argument.type, &argument_type));
653     MakeConstReferenceIfNeeded(&argument_type);
654     string argument_name = GetArgName("in", argument.name, ++argument_number);
655     argument_names.push_back(argument_name);
656     block.AddLine(StringPrintf(
657         "%s %s,", argument_type.c_str(), argument_name.c_str()));
658   }
659   vector<string> out_param_names{"response.get()", "error"};
660   for (const auto& argument : method.output_arguments) {
661     string argument_type;
662     CHECK(signature.Parse(argument.type, &argument_type));
663     string argument_name = GetArgName("out", argument.name, ++argument_number);
664     out_param_names.push_back(argument_name);
665     block.AddLine(StringPrintf(
666         "%s* %s,", argument_type.c_str(), argument_name.c_str()));
667   }
668   block.AddLine("brillo::ErrorPtr* error,");
669   block.AddLine(
670       StringPrintf("int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)%s",
671                    declaration_only ? " = 0;" : " override {"));
672   block.PopOffset();
673   if (!declaration_only) {
674     block.PushOffset(kBlockOffset);
675 
676     block.AddLine(
677         "auto response = brillo::dbus_utils::CallMethodAndBlockWithTimeout(");
678     block.PushOffset(kLineContinuationOffset);
679     block.AddLine("timeout_ms,");
680     block.AddLine("dbus_object_proxy_,");
681     block.AddLine(StringPrintf("\"%s\",", interface_name.c_str()));
682     block.AddLine(StringPrintf("\"%s\",", method.name.c_str()));
683     string last_argument = "error";
684     for (const auto& argument_name : argument_names) {
685       block.AddLine(StringPrintf("%s,", last_argument.c_str()));
686       last_argument = argument_name;
687     }
688     block.AddLine(StringPrintf("%s);", last_argument.c_str()));
689     block.PopOffset();
690 
691     block.AddLine("return response && "
692                   "brillo::dbus_utils::ExtractMethodCallResults(");
693     block.PushOffset(kLineContinuationOffset);
694     block.AddLine(brillo::string_utils::Join(", ", out_param_names) + ");");
695     block.PopOffset();
696     block.PopOffset();
697     block.AddLine("}");
698   }
699   text->AddBlock(block);
700 }
701 
702 // static
AddAsyncMethodProxy(const Interface::Method & method,const string & interface_name,bool declaration_only,IndentedText * text)703 void ProxyGenerator::AddAsyncMethodProxy(const Interface::Method& method,
704                                          const string& interface_name,
705                                          bool declaration_only,
706                                          IndentedText* text) {
707   IndentedText block;
708   DbusSignature signature;
709   block.AddBlankLine();
710   block.AddComments(method.doc_string);
711   block.AddLine(StringPrintf("%svoid %sAsync(",
712                              declaration_only ? "virtual " : "",
713                              method.name.c_str()));
714   block.PushOffset(kLineContinuationOffset);
715   vector<string> argument_names;
716   int argument_number = 0;
717   for (const auto& argument : method.input_arguments) {
718     string argument_type;
719     CHECK(signature.Parse(argument.type, &argument_type));
720     MakeConstReferenceIfNeeded(&argument_type);
721     string argument_name = GetArgName("in", argument.name, ++argument_number);
722     argument_names.push_back(argument_name);
723     block.AddLine(StringPrintf(
724         "%s %s,", argument_type.c_str(), argument_name.c_str()));
725   }
726   vector<string> out_params;
727   for (const auto& argument : method.output_arguments) {
728     string argument_type;
729     CHECK(signature.Parse(argument.type, &argument_type));
730     MakeConstReferenceIfNeeded(&argument_type);
731     if (!argument.name.empty())
732       base::StringAppendF(&argument_type, " /*%s*/", argument.name.c_str());
733     out_params.push_back(argument_type);
734   }
735   block.AddLine(StringPrintf(
736       "const base::Callback<void(%s)>& success_callback,",
737       brillo::string_utils::Join(", ", out_params).c_str()));
738   block.AddLine(
739       "const base::Callback<void(brillo::Error*)>& error_callback,");
740   block.AddLine(
741       StringPrintf("int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)%s",
742                    declaration_only ? " = 0;" : " override {"));
743   block.PopOffset();
744   if (!declaration_only) {
745     block.PushOffset(kBlockOffset);
746 
747     block.AddLine("brillo::dbus_utils::CallMethodWithTimeout(");
748     block.PushOffset(kLineContinuationOffset);
749     block.AddLine("timeout_ms,");
750     block.AddLine("dbus_object_proxy_,");
751     block.AddLine(StringPrintf("\"%s\",", interface_name.c_str()));
752     block.AddLine(StringPrintf("\"%s\",", method.name.c_str()));
753     block.AddLine("success_callback,");
754     string last_argument = "error_callback";
755     for (const auto& argument_name : argument_names) {
756       block.AddLine(StringPrintf("%s,", last_argument.c_str()));
757       last_argument = argument_name;
758     }
759     block.AddLine(StringPrintf("%s);", last_argument.c_str()));
760     block.PopOffset();
761 
762     block.PopOffset();
763     block.AddLine("}");
764   }
765   text->AddBlock(block);
766 }
767 
768 // static
AddMethodMock(const Interface::Method & method,const string &,IndentedText * text)769 void ProxyGenerator::AddMethodMock(const Interface::Method& method,
770                                    const string& /* interface_name */,
771                                    IndentedText* text) {
772   DbusSignature signature;
773   vector<string> arguments;
774   for (const auto& argument : method.input_arguments) {
775     string argument_type;
776     CHECK(signature.Parse(argument.type, &argument_type));
777     MakeConstReferenceIfNeeded(&argument_type);
778     if (!argument.name.empty())
779       base::StringAppendF(&argument_type, " /*in_%s*/", argument.name.c_str());
780     arguments.push_back(argument_type);
781   }
782   for (const auto& argument : method.output_arguments) {
783     string argument_type;
784     CHECK(signature.Parse(argument.type, &argument_type));
785     argument_type += '*';
786     if (!argument.name.empty())
787       base::StringAppendF(&argument_type, " /*out_%s*/", argument.name.c_str());
788     arguments.push_back(argument_type);
789   }
790   arguments.push_back("brillo::ErrorPtr* /*error*/");
791   arguments.push_back("int /*timeout_ms*/");
792   AddMockMethodDeclaration(method.name, "bool", arguments, text);
793 }
794 
795 // static
AddAsyncMethodMock(const Interface::Method & method,const string &,IndentedText * text)796 void ProxyGenerator::AddAsyncMethodMock(const Interface::Method& method,
797                                         const string& /* interface_name */,
798                                         IndentedText* text) {
799   DbusSignature signature;
800   vector<string> arguments;
801   for (const auto& argument : method.input_arguments) {
802     string argument_type;
803     CHECK(signature.Parse(argument.type, &argument_type));
804     MakeConstReferenceIfNeeded(&argument_type);
805     if (!argument.name.empty())
806       base::StringAppendF(&argument_type, " /*in_%s*/", argument.name.c_str());
807     arguments.push_back(argument_type);
808   }
809   vector<string> out_params;
810   for (const auto& argument : method.output_arguments) {
811     string argument_type;
812     CHECK(signature.Parse(argument.type, &argument_type));
813     MakeConstReferenceIfNeeded(&argument_type);
814     if (!argument.name.empty())
815       base::StringAppendF(&argument_type, " /*%s*/", argument.name.c_str());
816     out_params.push_back(argument_type);
817   }
818   arguments.push_back(StringPrintf(
819       "const base::Callback<void(%s)>& /*success_callback*/",
820       brillo::string_utils::Join(", ", out_params).c_str()));
821   arguments.push_back(
822       "const base::Callback<void(brillo::Error*)>& /*error_callback*/");
823   arguments.push_back("int /*timeout_ms*/");
824   AddMockMethodDeclaration(method.name + "Async", "void", arguments, text);
825 }
826 
AddMockMethodDeclaration(const string & method_name,const string & return_type,const vector<string> & arguments,IndentedText * text)827 void ProxyGenerator::AddMockMethodDeclaration(const string& method_name,
828                                               const string& return_type,
829                                               const vector<string>& arguments,
830                                               IndentedText* text) {
831   IndentedText block;
832   // GMOCK doesn't go all the way up to 11, so we need to handle methods with
833   // 11 arguments or more in a different way.
834   if (arguments.size() >= 11) {
835     block.AddLineAndPushOffsetTo(
836         StringPrintf("%s %s(%s,",
837                      return_type.c_str(),
838                      method_name.c_str(),
839                      arguments.front().c_str()),
840         1, '(');
841     for (size_t i = 1; i < arguments.size() - 1; i++)
842       block.AddLine(StringPrintf("%s,", arguments[i].c_str()));
843     block.AddLine(StringPrintf("%s) override {", arguments.back().c_str()));
844     block.PopOffset();
845     block.PushOffset(kBlockOffset);
846     block.AddLine(StringPrintf(
847         "LOG(WARNING) << \"%s(): gmock can't handle methods with %" PRIuS
848         " arguments. You can override this method in a subclass if you need"
849         " to.\";",
850         method_name.c_str(), arguments.size()));
851     if (return_type == "void") {
852       // No return added here.
853     } else if (return_type == "bool") {
854       block.AddLine("return false;");
855     } else {
856       LOG(FATAL) << "The return type is not supported.";
857     }
858     block.PopOffset();
859     block.AddLine("}");
860   } else {
861     block.AddLineAndPushOffsetTo(
862         StringPrintf("MOCK_METHOD%zu(%s,",
863                      arguments.size(), method_name.c_str()),
864         1, '(');
865     block.AddLineAndPushOffsetTo(
866         StringPrintf("%s(%s,", return_type.c_str(), arguments.front().c_str()),
867         1, '(');
868     for (size_t i = 1; i < arguments.size() - 1; i++)
869       block.AddLine(StringPrintf("%s,", arguments[i].c_str()));
870     block.AddLine(StringPrintf("%s));", arguments.back().c_str()));
871     block.PopOffset();
872     block.PopOffset();
873   }
874   text->AddBlock(block);
875 }
876 
877 // static
AddSignalHandlerRegistrationMock(const Interface::Signal & signal,IndentedText * text)878 void ProxyGenerator::AddSignalHandlerRegistrationMock(
879     const Interface::Signal& signal,
880     IndentedText* text) {
881   IndentedText callback_arg_text;
882   AddSignalCallbackArg(signal, true, &callback_arg_text);
883   vector<string> arg_lines = callback_arg_text.GetLines();
884 
885   IndentedText block;
886   block.AddLineAndPushOffsetTo(
887       StringPrintf("MOCK_METHOD2(Register%sSignalHandler,",
888                    signal.name.c_str()),
889       1, '(');
890   for (size_t i = 0; i < arg_lines.size(); ++i) {
891     if (i == 0)
892       block.AddLineAndPushOffsetTo("void(" + arg_lines[i], 1, '(');
893     else
894       block.AddLine(arg_lines[i]);
895   }
896   block.AddLine(
897       "dbus::ObjectProxy::OnConnectedCallback /*on_connected_callback*/));");
898   text->AddBlock(block);
899 }
900 
901 // static
AddSignalCallbackArg(const Interface::Signal & signal,bool comment_arg_name,IndentedText * block)902 void ProxyGenerator::AddSignalCallbackArg(const Interface::Signal& signal,
903                                           bool comment_arg_name,
904                                           IndentedText* block) {
905   DbusSignature signature;
906   string signal_callback = StringPrintf("%ssignal_callback%s",
907                                         comment_arg_name ? "/*" : "",
908                                         comment_arg_name ? "*/" : "");
909   if (signal.arguments.empty()) {
910     block->AddLine(StringPrintf("const base::Closure& %s,",
911                                 signal_callback.c_str()));
912   } else {
913     string last_argument;
914     string prefix{"const base::Callback<void("};
915     for (const auto argument : signal.arguments) {
916       if (!last_argument.empty()) {
917         if (!prefix.empty()) {
918           block->AddLineAndPushOffsetTo(
919               StringPrintf("%s%s,", prefix.c_str(), last_argument.c_str()),
920               1, '(');
921           prefix.clear();
922         } else {
923           block->AddLine(StringPrintf("%s,", last_argument.c_str()));
924         }
925       }
926       CHECK(signature.Parse(argument.type, &last_argument));
927       MakeConstReferenceIfNeeded(&last_argument);
928     }
929     block->AddLine(StringPrintf("%s%s)>& %s,",
930                                 prefix.c_str(),
931                                 last_argument.c_str(),
932                                 signal_callback.c_str()));
933     if (prefix.empty()) {
934       block->PopOffset();
935     }
936   }
937 }
938 
939 // static
GenerateProxy(const ServiceConfig & config,const std::vector<Interface> & interfaces,IndentedText * text)940 void ProxyGenerator::ObjectManager::GenerateProxy(
941     const ServiceConfig& config,
942     const std::vector<Interface>& interfaces,
943     IndentedText* text) {
944   if (config.object_manager.name.empty())
945     return;
946 
947   NameParser object_manager{config.object_manager.name};
948   object_manager.AddOpenNamespaces(text, false);
949   text->AddBlankLine();
950 
951   string class_name = object_manager.type_name + "Proxy";
952   text->AddLine(StringPrintf("class %s : "
953                              "public dbus::ObjectManager::Interface {",
954                              class_name.c_str()));
955   text->AddLineWithOffset("public:", kScopeOffset);
956   text->PushOffset(kBlockOffset);
957 
958   AddConstructor(config, class_name, interfaces, text);
959   AddDestructor(class_name, interfaces, text);
960   AddGetObjectManagerProxy(text);
961   for (const auto& itf : interfaces) {
962     AddInterfaceAccessors(itf, text);
963   }
964   text->PopOffset();
965 
966   text->AddLineWithOffset("private:", kScopeOffset);
967   text->PushOffset(kBlockOffset);
968   AddOnPropertyChanged(interfaces, text);
969   AddObjectAdded(config, interfaces, text);
970   AddObjectRemoved(interfaces, text);
971   AddCreateProperties(interfaces, class_name, text);
972   AddDataMembers(config, interfaces, class_name, text);
973 
974   text->AddLine(StringPrintf("DISALLOW_COPY_AND_ASSIGN(%s);",
975                               class_name.c_str()));
976   text->PopOffset();
977   text->AddLine("};");
978   text->AddBlankLine();
979   object_manager.AddCloseNamespaces(text, false);
980   text->AddBlankLine();
981 }
982 
AddConstructor(const ServiceConfig & config,const std::string & class_name,const std::vector<Interface> & interfaces,IndentedText * text)983 void ProxyGenerator::ObjectManager::AddConstructor(
984     const ServiceConfig& config,
985     const std::string& class_name,
986     const std::vector<Interface>& interfaces,
987     IndentedText* text) {
988   if (config.service_name.empty()) {
989     text->AddLineAndPushOffsetTo(
990         StringPrintf("%s(const scoped_refptr<dbus::Bus>& bus,",
991                      class_name.c_str()),
992         1, '(');
993     text->AddLine("const std::string& service_name)");
994     text->PopOffset();
995   } else {
996     text->AddLine(StringPrintf("%s(const scoped_refptr<dbus::Bus>& bus)",
997                                class_name.c_str()));
998   }
999   text->PushOffset(kLineContinuationOffset);
1000   text->AddLine(": bus_{bus},");
1001   text->PushOffset(kBlockOffset);
1002   if (config.service_name.empty()) {
1003     text->AddLine("service_name_{service_name},");
1004   }
1005   text->AddLine("dbus_object_manager_{bus->GetObjectManager(");
1006   text->PushOffset(kLineContinuationOffset);
1007   if (config.service_name.empty()) {
1008     text->AddLine("service_name,");
1009   } else {
1010     text->AddLine(StringPrintf("\"%s\",", config.service_name.c_str()));
1011   }
1012   text->AddLine(StringPrintf("dbus::ObjectPath{\"%s\"})} {",
1013                              config.object_manager.object_path.c_str()));
1014   text->PopOffset();
1015   text->PopOffset();
1016   text->PopOffset();
1017   text->PushOffset(kBlockOffset);
1018   for (const auto& itf : interfaces) {
1019     text->AddLine(
1020         StringPrintf("dbus_object_manager_->RegisterInterface(\"%s\", this);",
1021                      itf.name.c_str()));
1022   }
1023   text->PopOffset();
1024   text->AddLine("}");
1025   text->AddBlankLine();
1026 }
1027 
AddDestructor(const std::string & class_name,const std::vector<Interface> & interfaces,IndentedText * text)1028 void ProxyGenerator::ObjectManager::AddDestructor(
1029     const std::string& class_name,
1030     const std::vector<Interface>& interfaces,
1031     IndentedText* text) {
1032   text->AddLine(StringPrintf("~%s() override {", class_name.c_str()));
1033   text->PushOffset(kBlockOffset);
1034   for (const auto& itf : interfaces) {
1035     text->AddLine(
1036         StringPrintf("dbus_object_manager_->UnregisterInterface(\"%s\");",
1037                      itf.name.c_str()));
1038   }
1039   text->PopOffset();
1040   text->AddLine("}");
1041   text->AddBlankLine();
1042 }
1043 
AddGetObjectManagerProxy(IndentedText * text)1044 void ProxyGenerator::ObjectManager::AddGetObjectManagerProxy(
1045     IndentedText* text) {
1046   text->AddLine("dbus::ObjectManager* GetObjectManagerProxy() const {");
1047   text->AddLineWithOffset("return dbus_object_manager_;", kBlockOffset);
1048   text->AddLine("}");
1049   text->AddBlankLine();
1050 }
1051 
AddInterfaceAccessors(const Interface & interface,IndentedText * text)1052 void ProxyGenerator::ObjectManager::AddInterfaceAccessors(
1053     const Interface& interface,
1054     IndentedText* text) {
1055   NameParser itf_name{interface.name};
1056   string map_name = itf_name.MakeVariableName() + "_instances_";
1057 
1058   // GetProxy().
1059   if (interface.path.empty()) {
1060     // We have no fixed path, so there could be multiple instances of this itf.
1061     text->AddLine(StringPrintf("%sInterface* Get%s(",
1062                                 itf_name.MakeProxyName(true).c_str(),
1063                                 itf_name.MakeProxyName(false).c_str()));
1064     text->PushOffset(kLineContinuationOffset);
1065     text->AddLine("const dbus::ObjectPath& object_path) {");
1066     text->PopOffset();
1067     text->PushOffset(kBlockOffset);
1068     text->AddLine(StringPrintf("auto p = %s.find(object_path);",
1069                                 map_name.c_str()));
1070     text->AddLine(StringPrintf("if (p != %s.end())", map_name.c_str()));
1071     text->PushOffset(kBlockOffset);
1072     text->AddLine("return p->second.get();");
1073     text->PopOffset();
1074     text->AddLine("return nullptr;");
1075     text->PopOffset();
1076     text->AddLine("}");
1077   } else {
1078     // We have a fixed path, so the object could be considered a "singleton".
1079     // Skip the object_path parameter and return the first available instance.
1080     text->AddLine(StringPrintf("%sInterface* Get%s() {",
1081                                 itf_name.MakeProxyName(true).c_str(),
1082                                 itf_name.MakeProxyName(false).c_str()));
1083     text->PushOffset(kBlockOffset);
1084     text->AddLine(StringPrintf("if (%s.empty())", map_name.c_str()));
1085     text->AddLineWithOffset("return nullptr;", kBlockOffset);
1086     text->AddLine(StringPrintf("return %s.begin()->second.get();",
1087                                map_name.c_str()));
1088     text->PopOffset();
1089     text->AddLine("}");
1090   }
1091 
1092   // GetInstances().
1093   text->AddLine(
1094       StringPrintf("std::vector<%sInterface*> Get%sInstances() const {",
1095                    itf_name.MakeProxyName(true).c_str(),
1096                    itf_name.type_name.c_str()));
1097   text->PushOffset(kBlockOffset);
1098   text->AddLine(StringPrintf("std::vector<%sInterface*> values;",
1099                              itf_name.MakeProxyName(true).c_str()));
1100   text->AddLine(StringPrintf("values.reserve(%s.size());", map_name.c_str()));
1101   text->AddLine(StringPrintf("for (const auto& pair : %s)", map_name.c_str()));
1102   text->AddLineWithOffset("values.push_back(pair.second.get());", kBlockOffset);
1103   text->AddLine("return values;");
1104   text->PopOffset();
1105   text->AddLine("}");
1106 
1107   // SetAddedCallback().
1108   text->AddLine(StringPrintf("void Set%sAddedCallback(",
1109                               itf_name.type_name.c_str()));
1110   text->PushOffset(kLineContinuationOffset);
1111   text->AddLine(
1112       StringPrintf("const base::Callback<void(%sInterface*)>& callback) {",
1113                    itf_name.MakeProxyName(true).c_str()));
1114   text->PopOffset();
1115   text->PushOffset(kBlockOffset);
1116   text->AddLine(StringPrintf("on_%s_added_ = callback;",
1117                              itf_name.MakeVariableName().c_str()));
1118   text->PopOffset();
1119   text->AddLine("}");
1120 
1121   // SetRemovedCallback().
1122   text->AddLine(StringPrintf("void Set%sRemovedCallback(",
1123                              itf_name.type_name.c_str()));
1124   text->PushOffset(kLineContinuationOffset);
1125   text->AddLine("const base::Callback<void(const dbus::ObjectPath&)>& "
1126                 "callback) {");
1127   text->PopOffset();
1128   text->PushOffset(kBlockOffset);
1129   text->AddLine(StringPrintf("on_%s_removed_ = callback;",
1130                               itf_name.MakeVariableName().c_str()));
1131   text->PopOffset();
1132   text->AddLine("}");
1133 
1134   text->AddBlankLine();
1135 }
1136 
AddOnPropertyChanged(const std::vector<Interface> & interfaces,IndentedText * text)1137 void ProxyGenerator::ObjectManager::AddOnPropertyChanged(
1138     const std::vector<Interface>& interfaces,
1139     IndentedText* text) {
1140   // If there are no interfaces with properties, comment out parameter
1141   // names for OnPropertyChanged() to prevent compiler warnings on unused
1142   // function parameters.
1143   auto has_props = [](const Interface& itf) { return !itf.properties.empty(); };
1144   auto itf_with_props = std::find_if(interfaces.begin(), interfaces.end(),
1145                                      has_props);
1146   if (itf_with_props == interfaces.end()) {
1147     text->AddLineAndPushOffsetTo("void OnPropertyChanged("
1148                                  "const dbus::ObjectPath& /* object_path */,",
1149                                  1, '(');
1150     text->AddLine("const std::string& /* interface_name */,");
1151     text->AddLine("const std::string& /* property_name */) {}");
1152     text->PopOffset();
1153     text->AddBlankLine();
1154     return;
1155   }
1156   text->AddLineAndPushOffsetTo("void OnPropertyChanged("
1157                                "const dbus::ObjectPath& object_path,",
1158                                1, '(');
1159   text->AddLine("const std::string& interface_name,");
1160   text->AddLine("const std::string& property_name) {");
1161   text->PopOffset();
1162   text->PushOffset(kBlockOffset);
1163   for (const auto& itf : interfaces) {
1164     if (itf.properties.empty())
1165       continue;
1166 
1167     NameParser itf_name{itf.name};
1168     text->AddLine(StringPrintf("if (interface_name == \"%s\") {",
1169                                itf.name.c_str()));
1170     text->PushOffset(kBlockOffset);
1171     string map_name = itf_name.MakeVariableName() + "_instances_";
1172     text->AddLine(StringPrintf("auto p = %s.find(object_path);",
1173                                map_name.c_str()));
1174     text->AddLine(StringPrintf("if (p == %s.end())", map_name.c_str()));
1175     text->PushOffset(kBlockOffset);
1176     text->AddLine("return;");
1177     text->PopOffset();
1178     text->AddLine("p->second->OnPropertyChanged(property_name);");
1179     text->AddLine("return;");
1180     text->PopOffset();
1181     text->AddLine("}");
1182   }
1183   text->PopOffset();
1184   text->AddLine("}");
1185   text->AddBlankLine();
1186 }
1187 
AddObjectAdded(const ServiceConfig & config,const std::vector<Interface> & interfaces,IndentedText * text)1188 void ProxyGenerator::ObjectManager::AddObjectAdded(
1189     const ServiceConfig& config,
1190     const std::vector<Interface>& interfaces,
1191     IndentedText* text) {
1192   text->AddLine("void ObjectAdded(");
1193   text->PushOffset(kLineContinuationOffset);
1194   text->AddLine("const dbus::ObjectPath& object_path,");
1195   text->AddLine("const std::string& interface_name) override {");
1196   text->PopOffset();
1197   text->PushOffset(kBlockOffset);
1198   for (const auto& itf : interfaces) {
1199     NameParser itf_name{itf.name};
1200     string var_name = itf_name.MakeVariableName();
1201     text->AddLine(StringPrintf("if (interface_name == \"%s\") {",
1202                                itf.name.c_str()));
1203     text->PushOffset(kBlockOffset);
1204     if (!itf.properties.empty()) {
1205       text->AddLine("auto property_set =");
1206       text->PushOffset(kLineContinuationOffset);
1207       text->AddLine(StringPrintf("static_cast<%s::PropertySet*>(",
1208                                  itf_name.MakeProxyName(true).c_str()));
1209       text->PushOffset(kLineContinuationOffset);
1210       text->AddLine("dbus_object_manager_->GetProperties(object_path, "
1211                     "interface_name));");
1212       text->PopOffset();
1213       text->PopOffset();
1214     }
1215     text->AddLine(StringPrintf("std::unique_ptr<%s> %s_proxy{",
1216                                itf_name.MakeProxyName(true).c_str(),
1217                                var_name.c_str()));
1218     text->PushOffset(kBlockOffset);
1219     string new_instance = StringPrintf("new %s{bus_",
1220                                        itf_name.MakeProxyName(true).c_str());
1221     if (config.service_name.empty()) {
1222       new_instance += ", service_name_";
1223     }
1224     if (itf.path.empty())
1225       new_instance += ", object_path";
1226     if (!itf.properties.empty())
1227       new_instance += ", property_set";
1228     new_instance += "}";
1229     text->AddLine(new_instance);
1230     text->PopOffset();
1231     text->AddLine("};");
1232     text->AddLine(StringPrintf("auto p = %s_instances_.emplace(object_path, "
1233                                "std::move(%s_proxy));",
1234                                var_name.c_str(), var_name.c_str()));
1235     text->AddLine(StringPrintf("if (!on_%s_added_.is_null())",
1236                                var_name.c_str()));
1237     text->PushOffset(kBlockOffset);
1238     text->AddLine(StringPrintf("on_%s_added_.Run(p.first->second.get());",
1239                                var_name.c_str()));
1240     text->PopOffset();
1241     text->AddLine("return;");
1242     text->PopOffset();
1243     text->AddLine("}");
1244   }
1245   text->PopOffset();
1246   text->AddLine("}");
1247   text->AddBlankLine();
1248 }
1249 
AddObjectRemoved(const std::vector<Interface> & interfaces,IndentedText * text)1250 void ProxyGenerator::ObjectManager::AddObjectRemoved(
1251     const std::vector<Interface>& interfaces,
1252     IndentedText* text) {
1253   text->AddLine("void ObjectRemoved(");
1254   text->PushOffset(kLineContinuationOffset);
1255   text->AddLine("const dbus::ObjectPath& object_path,");
1256   text->AddLine("const std::string& interface_name) override {");
1257   text->PopOffset();
1258   text->PushOffset(kBlockOffset);
1259   for (const auto& itf : interfaces) {
1260     NameParser itf_name{itf.name};
1261     string var_name = itf_name.MakeVariableName();
1262     text->AddLine(StringPrintf("if (interface_name == \"%s\") {",
1263                                itf.name.c_str()));
1264     text->PushOffset(kBlockOffset);
1265     text->AddLine(StringPrintf("auto p = %s_instances_.find(object_path);",
1266                                var_name.c_str()));
1267     text->AddLine(StringPrintf("if (p != %s_instances_.end()) {",
1268                                var_name.c_str()));
1269     text->PushOffset(kBlockOffset);
1270     text->AddLine(StringPrintf("if (!on_%s_removed_.is_null())",
1271                                var_name.c_str()));
1272     text->PushOffset(kBlockOffset);
1273     text->AddLine(StringPrintf("on_%s_removed_.Run(object_path);",
1274                                var_name.c_str()));
1275     text->PopOffset();
1276     text->AddLine(StringPrintf("%s_instances_.erase(p);",
1277                                var_name.c_str()));
1278     text->PopOffset();
1279     text->AddLine("}");
1280     text->AddLine("return;");
1281     text->PopOffset();
1282     text->AddLine("}");
1283   }
1284   text->PopOffset();
1285   text->AddLine("}");
1286   text->AddBlankLine();
1287 }
1288 
AddCreateProperties(const std::vector<Interface> & interfaces,const std::string & class_name,IndentedText * text)1289 void ProxyGenerator::ObjectManager::AddCreateProperties(
1290     const std::vector<Interface>& interfaces,
1291     const std::string& class_name,
1292     IndentedText* text) {
1293   text->AddLine("dbus::PropertySet* CreateProperties(");
1294   text->PushOffset(kLineContinuationOffset);
1295   text->AddLine("dbus::ObjectProxy* object_proxy,");
1296   text->AddLine("const dbus::ObjectPath& object_path,");
1297   text->AddLine("const std::string& interface_name) override {");
1298   text->PopOffset();
1299   text->PushOffset(kBlockOffset);
1300   for (const auto& itf : interfaces) {
1301     NameParser itf_name{itf.name};
1302     text->AddLine(StringPrintf("if (interface_name == \"%s\") {",
1303                                itf.name.c_str()));
1304     text->PushOffset(kBlockOffset);
1305     text->AddLine(StringPrintf("return new %s::PropertySet{",
1306                                itf_name.MakeProxyName(true).c_str()));
1307     text->PushOffset(kLineContinuationOffset);
1308     text->AddLine("object_proxy,");
1309     text->AddLineAndPushOffsetTo(
1310         StringPrintf("base::Bind(&%s::OnPropertyChanged,",
1311                      class_name.c_str()),
1312         1, '(');
1313     text->AddLine("weak_ptr_factory_.GetWeakPtr(),");
1314     text->AddLine("object_path,");
1315     text->AddLine("interface_name)");
1316     text->PopOffset();
1317     text->PopOffset();
1318     text->AddLine("};");
1319     text->PopOffset();
1320     text->AddLine("}");
1321   }
1322   text->AddLineAndPushOffsetTo("LOG(FATAL) << \"Creating properties for "
1323                                "unsupported interface \"", 1, ' ');
1324   text->AddLine("<< interface_name;");
1325   text->PopOffset();
1326   text->AddLine("return nullptr;");
1327   text->PopOffset();
1328   text->AddLine("}");
1329   text->AddBlankLine();
1330 }
1331 
AddDataMembers(const ServiceConfig & config,const std::vector<Interface> & interfaces,const std::string & class_name,IndentedText * text)1332 void ProxyGenerator::ObjectManager::AddDataMembers(
1333     const ServiceConfig& config,
1334     const std::vector<Interface>& interfaces,
1335     const std::string& class_name,
1336     IndentedText* text) {
1337   text->AddLine("scoped_refptr<dbus::Bus> bus_;");
1338   if (config.service_name.empty()) {
1339     text->AddLine("std::string service_name_;");
1340   }
1341   text->AddLine("dbus::ObjectManager* dbus_object_manager_;");
1342   for (const auto& itf : interfaces) {
1343     NameParser itf_name{itf.name};
1344     string var_name = itf_name.MakeVariableName();
1345     text->AddLineAndPushOffsetTo("std::map<dbus::ObjectPath,", 1, '<');
1346     text->AddLine(StringPrintf("std::unique_ptr<%s>> %s_instances_;",
1347                                itf_name.MakeProxyName(true).c_str(),
1348                                var_name.c_str()));
1349     text->PopOffset();
1350     text->AddLine(
1351         StringPrintf("base::Callback<void(%sInterface*)> on_%s_added_;",
1352                      itf_name.MakeProxyName(true).c_str(),
1353                      var_name.c_str()));
1354     text->AddLine(StringPrintf("base::Callback<void(const dbus::ObjectPath&)> "
1355                                "on_%s_removed_;",
1356                                var_name.c_str()));
1357   }
1358   text->AddLine(
1359       StringPrintf("base::WeakPtrFactory<%s> weak_ptr_factory_{this};",
1360                    class_name.c_str()));
1361   text->AddBlankLine();
1362 }
1363 
1364 // static
GetHandlerNameForSignal(const string & signal)1365 string ProxyGenerator::GetHandlerNameForSignal(const string& signal) {
1366   return StringPrintf("On%sSignal", signal.c_str());
1367 }
1368 
1369 }  // namespace chromeos_dbus_bindings
1370