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