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
19using base::StringPrintf;
20using std::string;
21using std::vector;
22
23namespace chromeos_dbus_bindings {
24
25// static
26bool 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
60void 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
125void 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
133void 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
151void 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
237void 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
326void 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
378void 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
416void 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
480void 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