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/adaptor_generator.h"
6 
7 #include <string>
8 
9 #include <base/logging.h>
10 #include <base/strings/string_util.h>
11 #include <base/strings/stringprintf.h>
12 #include <brillo/strings/string_utils.h>
13 
14 #include "chromeos-dbus-bindings/dbus_signature.h"
15 #include "chromeos-dbus-bindings/indented_text.h"
16 #include "chromeos-dbus-bindings/interface.h"
17 #include "chromeos-dbus-bindings/name_parser.h"
18 
19 using base::StringPrintf;
20 using std::string;
21 using std::vector;
22 
23 namespace chromeos_dbus_bindings {
24 
25 // static
GenerateAdaptors(const std::vector<Interface> & interfaces,const base::FilePath & output_file)26 bool AdaptorGenerator::GenerateAdaptors(
27     const std::vector<Interface>& interfaces,
28     const base::FilePath& output_file) {
29   IndentedText text;
30   CHECK(!interfaces.empty()) << "At least one interface must be provided";
31 
32   text.AddLine("// Automatic generation of D-Bus interfaces:");
33   for (const auto& interface : interfaces) {
34     text.AddLine(StringPrintf("//  - %s", interface.name.c_str()));
35   }
36   string header_guard = GenerateHeaderGuard(output_file);
37   text.AddLine(StringPrintf("#ifndef %s", header_guard.c_str()));
38   text.AddLine(StringPrintf("#define %s", header_guard.c_str()));
39   text.AddLine("#include <memory>");
40   text.AddLine("#include <string>");
41   text.AddLine("#include <tuple>");
42   text.AddLine("#include <vector>");
43   text.AddBlankLine();
44   text.AddLine("#include <base/macros.h>");
45   text.AddLine("#include <dbus/object_path.h>");
46   text.AddLine("#include <brillo/any.h>");
47   text.AddLine("#include <brillo/dbus/dbus_object.h>");
48   text.AddLine("#include <brillo/dbus/exported_object_manager.h>");
49   text.AddLine("#include <brillo/variant_dictionary.h>");
50 
51   for (const auto& interface : interfaces)
52     GenerateInterfaceAdaptor(interface, &text);
53 
54   text.AddLine(StringPrintf("#endif  // %s", header_guard.c_str()));
55 
56   return WriteTextToFile(output_file, text);
57 }
58 
59 // static
GenerateInterfaceAdaptor(const Interface & interface,IndentedText * text)60 void AdaptorGenerator::GenerateInterfaceAdaptor(
61     const Interface& interface,
62     IndentedText *text) {
63   NameParser parser{interface.name};
64   string itf_name = parser.MakeInterfaceName(false);
65   string class_name = parser.MakeAdaptorName(false);
66   string full_itf_name = parser.MakeFullCppName();
67 
68   text->AddBlankLine();
69   parser.AddOpenNamespaces(text, false);
70 
71   text->AddBlankLine();
72   text->AddLine(StringPrintf("// Interface definition for %s.",
73                              full_itf_name.c_str()));
74   text->AddComments(interface.doc_string);
75   text->AddLine(StringPrintf("class %s {", itf_name.c_str()));
76   text->AddLineWithOffset("public:", kScopeOffset);
77   text->PushOffset(kBlockOffset);
78   text->AddLine(StringPrintf("virtual ~%s() = default;", itf_name.c_str()));
79   AddInterfaceMethods(interface, text);
80   text->PopOffset();
81   text->AddLine("};");
82 
83   text->AddBlankLine();
84   text->AddLine(StringPrintf("// Interface adaptor for %s.",
85                              full_itf_name.c_str()));
86   text->AddLine(StringPrintf("class %s {", class_name.c_str()));
87   text->AddLineWithOffset("public:", kScopeOffset);
88   text->PushOffset(kBlockOffset);
89   AddConstructor(class_name, itf_name, text);
90   AddRegisterWithDBusObject(itf_name, interface, text);
91   AddSendSignalMethods(interface, text);
92   AddPropertyMethodImplementation(interface, text);
93   if (!interface.path.empty()) {
94     text->AddBlankLine();
95     text->AddLine("static dbus::ObjectPath GetObjectPath() {");
96     text->PushOffset(kBlockOffset);
97     text->AddLine(StringPrintf("return dbus::ObjectPath{\"%s\"};",
98                                interface.path.c_str()));
99     text->PopOffset();
100     text->AddLine("}");
101   }
102   text->PopOffset();
103 
104   text->AddBlankLine();
105   text->AddLineWithOffset("private:", kScopeOffset);
106   text->PushOffset(kBlockOffset);
107   AddSignalDataMembers(interface, text);
108   AddPropertyDataMembers(interface, text);
109 
110   text->AddLine(StringPrintf(
111       "%s* interface_;  // Owned by container of this adapter.",
112       itf_name.c_str()));
113 
114   text->AddBlankLine();
115   text->AddLine(StringPrintf("DISALLOW_COPY_AND_ASSIGN(%s);",
116                              class_name.c_str()));
117   text->PopOffset();
118   text->AddLine("};");
119 
120   text->AddBlankLine();
121   parser.AddCloseNamespaces(text, false);
122 }
123 
124 // static
AddConstructor(const string & class_name,const string & itf_name,IndentedText * text)125 void AdaptorGenerator::AddConstructor(const string& class_name,
126                                       const string& itf_name,
127                                       IndentedText *text) {
128   text->AddLine(StringPrintf("%s(%s* interface) : interface_(interface) {}",
129                              class_name.c_str(), itf_name.c_str()));
130 }
131 
132 // static
AddRegisterWithDBusObject(const std::string & itf_name,const Interface & interface,IndentedText * text)133 void AdaptorGenerator::AddRegisterWithDBusObject(
134     const std::string& itf_name,
135     const Interface& interface,
136     IndentedText *text) {
137   text->AddBlankLine();
138   text->AddLine(
139     "void RegisterWithDBusObject(brillo::dbus_utils::DBusObject* object) {");
140   text->PushOffset(kBlockOffset);
141   text->AddLine("brillo::dbus_utils::DBusInterface* itf =");
142   text->AddLineWithOffset(
143       StringPrintf("object->AddOrGetInterface(\"%s\");",
144                    interface.name.c_str()), kLineContinuationOffset);
145   RegisterInterface(itf_name, interface, text);
146   text->PopOffset();
147   text->AddLine("}");
148 }
149 
150 // static
RegisterInterface(const string & itf_name,const Interface & interface,IndentedText * text)151 void AdaptorGenerator::RegisterInterface(const string& itf_name,
152                                          const Interface& interface,
153                                          IndentedText *text) {
154   if (!interface.methods.empty())
155     text->AddBlankLine();
156   for (const auto& method : interface.methods) {
157     string add_handler_name;
158     switch (method.kind) {
159       case Interface::Method::Kind::kSimple:
160         add_handler_name = "AddSimpleMethodHandler";
161         break;
162       case Interface::Method::Kind::kNormal:
163         if (method.include_dbus_message)
164           add_handler_name = "AddSimpleMethodHandlerWithErrorAndMessage";
165         else
166           add_handler_name = "AddSimpleMethodHandlerWithError";
167         break;
168       case Interface::Method::Kind::kAsync:
169         if (method.include_dbus_message)
170           add_handler_name = "AddMethodHandlerWithMessage";
171         else
172           add_handler_name = "AddMethodHandler";
173         break;
174       case Interface::Method::Kind::kRaw:
175         add_handler_name = "AddRawMethodHandler";
176         break;
177     }
178 
179     text->AddLine(StringPrintf("itf->%s(", add_handler_name.c_str()));
180     text->PushOffset(kLineContinuationOffset);
181     text->AddLine(StringPrintf("\"%s\",", method.name.c_str()));
182     text->AddLine("base::Unretained(interface_),");
183     text->AddLine(StringPrintf("&%s::%s);", itf_name.c_str(),
184                                method.name.c_str()));
185     text->PopOffset();
186   }
187 
188   // Register signals.
189   if (!interface.signals.empty())
190     text->AddBlankLine();
191   for (const auto& signal : interface.signals) {
192     string signal_var_name = StringPrintf("signal_%s_", signal.name.c_str());
193     string signal_type_name = StringPrintf("Signal%sType", signal.name.c_str());
194     text->AddLine(StringPrintf("%s = itf->RegisterSignalOfType<%s>(\"%s\");",
195                                signal_var_name.c_str(),
196                                signal_type_name.c_str(),
197                                signal.name.c_str()));
198   }
199 
200   // Register exported properties.
201   if (!interface.properties.empty())
202     text->AddBlankLine();
203   for (const auto& property : interface.properties) {
204     string variable_name = NameParser{property.name}.MakeVariableName();
205     string write_access;
206     if (property.access == "write") {
207       write_access = "kWriteOnly";
208     } else if (property.access == "readwrite") {
209       write_access = "kReadWrite";
210     }
211     if (!write_access.empty()) {
212       text->AddLine(StringPrintf("%s_.SetAccessMode(", variable_name.c_str()));
213       text->PushOffset(kLineContinuationOffset);
214       text->AddLine(
215           StringPrintf(
216               "brillo::dbus_utils::ExportedPropertyBase::Access::%s);",
217               write_access.c_str()));
218       text->PopOffset();
219       text->AddLine(StringPrintf("%s_.SetValidator(", variable_name.c_str()));
220       text->PushOffset(kLineContinuationOffset);
221       text->AddLineAndPushOffsetTo(
222           StringPrintf(
223               "base::Bind(&%s::Validate%s,",
224               NameParser{interface.name}.MakeAdaptorName(false).c_str(),
225               property.name.c_str()),
226           1, '(');
227       text->AddLine("base::Unretained(this)));");
228       text->PopOffset();
229       text->PopOffset();
230     }
231     text->AddLine(StringPrintf("itf->AddProperty(%sName(), &%s_);",
232                                property.name.c_str(), variable_name.c_str()));
233   }
234 }
235 
236 // static
AddInterfaceMethods(const Interface & interface,IndentedText * text)237 void AdaptorGenerator::AddInterfaceMethods(const Interface& interface,
238                                            IndentedText *text) {
239   IndentedText block;
240   DbusSignature signature;
241   if (!interface.methods.empty())
242     block.AddBlankLine();
243 
244   for (const auto& method : interface.methods) {
245     string const_method;
246     if (method.is_const)
247       const_method = " const";
248 
249     string return_type = "void";
250     vector<string> method_params;
251     auto input_arguments_copy = method.input_arguments;
252     auto output_arguments_copy = method.output_arguments;
253     switch (method.kind) {
254       case Interface::Method::Kind::kSimple:
255         if (output_arguments_copy.size() == 1) {
256           CHECK(signature.Parse(output_arguments_copy[0].type, &return_type));
257           output_arguments_copy.clear();
258         }
259         break;
260       case Interface::Method::Kind::kNormal:
261         method_params.push_back("brillo::ErrorPtr* error");
262         if (method.include_dbus_message)
263           method_params.push_back("dbus::Message* message");
264         return_type = "bool";
265         break;
266       case Interface::Method::Kind::kAsync: {
267         std::vector<std::string> out_types;
268         for (const auto& argument : output_arguments_copy) {
269           string param_type;
270           CHECK(signature.Parse(argument.type, &param_type));
271           out_types.push_back(param_type);
272         }
273         method_params.push_back(base::StringPrintf(
274             "std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<%s>> "
275             "response",
276              brillo::string_utils::Join(", ", out_types).c_str()));
277         if (method.include_dbus_message)
278           method_params.push_back("dbus::Message* message");
279         output_arguments_copy.clear();
280         break;
281       }
282       case Interface::Method::Kind::kRaw:
283         method_params.push_back("dbus::MethodCall* method_call");
284         method_params.push_back("brillo::dbus_utils::ResponseSender sender");
285         // Raw methods don't take static parameters or return values directly.
286         input_arguments_copy.clear();
287         output_arguments_copy.clear();
288         break;
289     }
290     block.AddComments(method.doc_string);
291     string method_start = StringPrintf("virtual %s %s(",
292                                        return_type.c_str(),
293                                        method.name.c_str());
294     string method_end = StringPrintf(")%s = 0;", const_method.c_str());
295     int index = 0;
296     for (const auto& argument : input_arguments_copy) {
297       string param_type;
298       CHECK(signature.Parse(argument.type, &param_type));
299       MakeConstReferenceIfNeeded(&param_type);
300       string param_name = GetArgName("in", argument.name, ++index);
301       method_params.push_back(param_type + ' ' + param_name);
302     }
303 
304     for (const auto& argument : output_arguments_copy) {
305       string param_type;
306       CHECK(signature.Parse(argument.type, &param_type));
307       string param_name = GetArgName("out", argument.name, ++index);
308       method_params.push_back(param_type + "* " + param_name);
309     }
310 
311     if (method_params.empty()) {
312       block.AddLine(method_start + method_end);
313     } else {
314       block.AddLine(method_start);
315       block.PushOffset(kLineContinuationOffset);
316       for (size_t i = 0; i < method_params.size() - 1; i++)
317         block.AddLine(method_params[i] + ',');
318       block.AddLine(method_params.back() + method_end);
319       block.PopOffset();
320     }
321   }
322   text->AddBlock(block);
323 }
324 
325 // static
AddSendSignalMethods(const Interface & interface,IndentedText * text)326 void AdaptorGenerator::AddSendSignalMethods(
327     const Interface& interface,
328     IndentedText *text) {
329   IndentedText block;
330   DbusSignature signature;
331 
332   if (!interface.signals.empty())
333     block.AddBlankLine();
334 
335   for (const auto& signal : interface.signals) {
336     block.AddComments(signal.doc_string);
337     string method_start = StringPrintf("void Send%sSignal(",
338                                        signal.name.c_str());
339     string method_end = ") {";
340 
341     int index = 0;
342     vector<string> method_params;
343     vector<string> param_names;
344     for (const auto& argument : signal.arguments) {
345       string param_type;
346       CHECK(signature.Parse(argument.type, &param_type));
347       MakeConstReferenceIfNeeded(&param_type);
348       string param_name = GetArgName("in", argument.name, ++index);
349       param_names.push_back(param_name);
350       method_params.push_back(param_type + ' ' + param_name);
351     }
352 
353     if (method_params.empty()) {
354       block.AddLine(method_start + method_end);
355     } else {
356       block.AddLine(method_start);
357       block.PushOffset(kLineContinuationOffset);
358       for (size_t i = 0; i < method_params.size() - 1; i++)
359         block.AddLine(method_params[i] + ',');
360       block.AddLine(method_params.back() + method_end);
361       block.PopOffset();
362     }
363 
364     string args = brillo::string_utils::Join(", ", param_names);
365     block.PushOffset(kBlockOffset);
366     block.AddLine(StringPrintf("auto signal = signal_%s_.lock();",
367                                 signal.name.c_str()));
368     block.AddLine("if (signal)");
369     block.AddLineWithOffset(StringPrintf("signal->Send(%s);", args.c_str()),
370                             kBlockOffset);
371     block.PopOffset();
372     block.AddLine("}");
373   }
374   text->AddBlock(block);
375 }
376 
377 // static
AddSignalDataMembers(const Interface & interface,IndentedText * text)378 void AdaptorGenerator::AddSignalDataMembers(const Interface& interface,
379                                             IndentedText *text) {
380   IndentedText block;
381   DbusSignature signature;
382 
383   for (const auto& signal : interface.signals) {
384     string signal_type_name = StringPrintf("Signal%sType", signal.name.c_str());
385     string signal_type_alias_begin =
386         StringPrintf("using %s = brillo::dbus_utils::DBusSignal<",
387                      signal_type_name.c_str());
388     string signal_type_alias_end = ">;";
389     vector<string> params;
390     for (const auto& argument : signal.arguments) {
391       string param;
392       CHECK(signature.Parse(argument.type, &param));
393       if (!argument.name.empty())
394         base::StringAppendF(&param, " /*%s*/", argument.name.c_str());
395       params.push_back(param);
396     }
397     if (params.empty()) {
398       block.AddLine(signal_type_alias_begin + signal_type_alias_end);
399     } else {
400       block.AddLine(signal_type_alias_begin);
401       block.PushOffset(kLineContinuationOffset);
402       for (size_t i = 0; i < params.size() - 1; i++)
403         block.AddLine(params[i] + ',');
404       block.AddLine(params.back() + signal_type_alias_end);
405       block.PopOffset();
406     }
407     block.AddLine(
408         StringPrintf("std::weak_ptr<%s> signal_%s_;",
409                       signal_type_name.c_str(), signal.name.c_str()));
410     block.AddBlankLine();
411   }
412   text->AddBlock(block);
413 }
414 
415 // static
AddPropertyMethodImplementation(const Interface & interface,IndentedText * text)416 void AdaptorGenerator::AddPropertyMethodImplementation(
417     const Interface& interface,
418     IndentedText *text) {
419   IndentedText block;
420   DbusSignature signature;
421 
422   for (const auto& property : interface.properties) {
423     block.AddBlankLine();
424     string type;
425     CHECK(signature.Parse(property.type, &type));
426     string variable_name = NameParser{property.name}.MakeVariableName();
427 
428     // Property name accessor.
429     block.AddComments(property.doc_string);
430     block.AddLine(StringPrintf("static const char* %sName() { return \"%s\"; }",
431                                property.name.c_str(), property.name.c_str()));
432 
433     // Getter method.
434     block.AddLine(StringPrintf("%s Get%s() const {",
435                                type.c_str(),
436                                property.name.c_str()));
437     block.PushOffset(kBlockOffset);
438     block.AddLine(StringPrintf("return %s_.GetValue().Get<%s>();",
439                                variable_name.c_str(),
440                                type.c_str()));
441     block.PopOffset();
442     block.AddLine("}");
443 
444     // Setter method.
445     MakeConstReferenceIfNeeded(&type);
446     block.AddLine(StringPrintf("void Set%s(%s %s) {",
447                                property.name.c_str(),
448                                type.c_str(),
449                                variable_name.c_str()));
450     block.PushOffset(kBlockOffset);
451     block.AddLine(StringPrintf("%s_.SetValue(%s);",
452                                variable_name.c_str(),
453                                variable_name.c_str()));
454     block.PopOffset();
455     block.AddLine("}");
456 
457     // Validation method for property with write access.
458     if (property.access != "read") {
459       CHECK(signature.Parse(property.type, &type));
460       block.AddLine(StringPrintf("virtual bool Validate%s(",
461                                  property.name.c_str()));
462       block.PushOffset(kLineContinuationOffset);
463       // Explicitly specify the "value" parameter as const & to match the
464       // validator callback function signature.
465       block.AddLine(
466           StringPrintf(
467               "brillo::ErrorPtr* /*error*/, const %s& /*value*/) {",
468               type.c_str()));
469       block.PopOffset();
470       block.PushOffset(kBlockOffset);
471       block.AddLine("return true;");
472       block.PopOffset();
473       block.AddLine("}");
474     }
475   }
476   text->AddBlock(block);
477 }
478 
479 // static
AddPropertyDataMembers(const Interface & interface,IndentedText * text)480 void AdaptorGenerator::AddPropertyDataMembers(const Interface& interface,
481                                               IndentedText *text) {
482   IndentedText block;
483   DbusSignature signature;
484 
485   for (const auto& property : interface.properties) {
486     string type;
487     CHECK(signature.Parse(property.type, &type));
488     string variable_name = NameParser{property.name}.MakeVariableName();
489 
490     block.AddLine(
491         StringPrintf("brillo::dbus_utils::ExportedProperty<%s> %s_;",
492                      type.c_str(), variable_name.c_str()));
493   }
494   if (!interface.properties.empty())
495     block.AddBlankLine();
496 
497   text->AddBlock(block);
498 }
499 
500 }  // namespace chromeos_dbus_bindings
501