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 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 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 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 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 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 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 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 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 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 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