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, ¶m_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, ¶m_type));
299 MakeConstReferenceIfNeeded(¶m_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, ¶m_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, ¶m_type));
347 MakeConstReferenceIfNeeded(¶m_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, ¶m));
393 if (!argument.name.empty())
394 base::StringAppendF(¶m, " /*%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